Update stable to r4461.

git-svn-id: http://webrtc.googlecode.com/svn/stable/talk@4462 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/OWNERS b/OWNERS
index 159a05d..b55b3a3 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,3 +1,4 @@
+set noparent
 fischman@webrtc.org
 henrike@webrtc.org
 hta@webrtc.org
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 447b46d..fc29f95 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -23,6 +23,37 @@
 # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+# List of files that should not be committed to
+DO_NOT_SUBMIT_FILES = [
+    "talk/app/webrtc/mediaconstraintsinterface.h",
+    "talk/app/webrtc/webrtcsdp_unittest.cc",
+    "talk/base/linux.cc",
+    "talk/base/linux.h",
+    "talk/base/linux_unittest.cc",
+    "talk/main.scons",
+    "talk/media/base/hybridvideoengine.cc",
+    "talk/media/base/mediaengine.cc",
+    "talk/media/base/mutedvideocapturer.cc",
+    "talk/media/base/streamparams.h",
+    "talk/media/base/videocapturer.cc",
+    "talk/media/base/videocapturer.h",
+    "talk/media/base/videocapturer_unittest.cc",
+    "talk/media/base/videoengine_unittest.h",
+    "talk/media/devices/devicemanager.cc",
+    "talk/media/webrtc/fakewebrtcvideoengine.h",
+    "talk/media/webrtc/fakewebrtcvoiceengine.h",
+    "talk/media/webrtc/webrtcexport.h",
+    "talk/media/webrtc/webrtcmediaengine.h",
+    "talk/media/webrtc/webrtcvideoengine.cc",
+    "talk/media/webrtc/webrtcvideoengine.h",
+    "talk/media/webrtc/webrtcvideoengine_unittest.cc",
+    "talk/media/webrtc/webrtcvoiceengine.cc",
+    "talk/media/webrtc/webrtcvoiceengine.h",
+    "talk/media/webrtc/webrtcvoiceengine_unittest.cc",
+    "talk/p2p/base/session.cc",
+    "talk/session/media/channel.cc",
+    "talk/session/media/mediasession_unittest.cc"]
+
 def _LicenseHeader(input_api):
   """Returns the license header regexp."""
   # Accept any year number from 2008 to the current year
@@ -75,11 +106,26 @@
   }
   return license_header
 
+def _ProtectedFiles(input_api, output_api):
+  results = []
+  changed_files = []
+  for f in input_api.AffectedFiles():
+    changed_files.append(f.LocalPath())
+  bad_files = list(set(DO_NOT_SUBMIT_FILES) & set(changed_files))
+  if bad_files:
+    error_type = output_api.PresubmitError
+    results.append(error_type(
+        'The following affected files are only allowed to be updated when '
+        'importing libjingle',
+        bad_files))
+  return results
+
 def _CommonChecks(input_api, output_api):
   """Checks common to both upload and commit."""
   results = []
   results.extend(input_api.canned_checks.CheckLicense(
       input_api, output_api, _LicenseHeader(input_api)))
+  results.extend(_ProtectedFiles(input_api, output_api))
   return results
 
 def CheckChangeOnUpload(input_api, output_api):
diff --git a/app/webrtc/datachannel.cc b/app/webrtc/datachannel.cc
index 345cd5f..4972424 100644
--- a/app/webrtc/datachannel.cc
+++ b/app/webrtc/datachannel.cc
@@ -34,7 +34,8 @@
 
 namespace webrtc {
 
-static size_t kMaxQueuedDataPackets = 100;
+static size_t kMaxQueuedReceivedDataPackets = 100;
+static size_t kMaxQueuedSendDataPackets = 100;
 
 talk_base::scoped_refptr<DataChannel> DataChannel::Create(
     WebRtcSession* session,
@@ -95,12 +96,13 @@
 }
 
 DataChannel::~DataChannel() {
-  ClearQueuedData();
+  ClearQueuedReceivedData();
+  ClearQueuedSendData();
 }
 
 void DataChannel::RegisterObserver(DataChannelObserver* observer) {
   observer_ = observer;
-  DeliverQueuedData();
+  DeliverQueuedReceivedData();
 }
 
 void DataChannel::UnregisterObserver() {
@@ -117,7 +119,13 @@
 }
 
 uint64 DataChannel::buffered_amount() const {
-  return 0;
+  uint64 buffered_amount = 0;
+  for (std::deque<DataBuffer*>::const_iterator it = queued_send_data_.begin();
+      it != queued_send_data_.end();
+      ++it) {
+    buffered_amount += (*it)->size();
+  }
+  return buffered_amount;
 }
 
 void DataChannel::Close() {
@@ -133,20 +141,22 @@
   if (state_ != kOpen) {
     return false;
   }
-  cricket::SendDataParams send_params;
-
-  send_params.ssrc = send_ssrc_;
-  if (session_->data_channel_type() == cricket::DCT_SCTP) {
-    send_params.ordered = config_.ordered;
-    send_params.max_rtx_count = config_.maxRetransmits;
-    send_params.max_rtx_ms = config_.maxRetransmitTime;
+  // If the queue is non-empty, we're waiting for SignalReadyToSend,
+  // so just add to the end of the queue and keep waiting.
+  if (!queued_send_data_.empty()) {
+    return QueueSendData(buffer);
   }
-  send_params.type = buffer.binary ? cricket::DMT_BINARY : cricket::DMT_TEXT;
 
   cricket::SendDataResult send_result;
-  // TODO(pthatcher): Use send_result.would_block for buffering.
-  return session_->data_channel()->SendData(
-      send_params, buffer.data, &send_result);
+  if (!InternalSendWithoutQueueing(buffer, &send_result)) {
+    if (send_result == cricket::SDR_BLOCK) {
+      return QueueSendData(buffer);
+    }
+    // Fail for other results.
+    // TODO(jiayl): We should close the data channel in this case.
+    return false;
+  }
+  return true;
 }
 
 void DataChannel::SetReceiveSsrc(uint32 receive_ssrc) {
@@ -183,6 +193,43 @@
   DoClose();
 }
 
+void DataChannel::OnDataReceived(cricket::DataChannel* channel,
+                                 const cricket::ReceiveDataParams& params,
+                                 const talk_base::Buffer& payload) {
+  if (params.ssrc != receive_ssrc_) {
+    return;
+  }
+
+  bool binary = (params.type == cricket::DMT_BINARY);
+  talk_base::scoped_ptr<DataBuffer> buffer(new DataBuffer(payload, binary));
+  if (was_ever_writable_ && observer_) {
+    observer_->OnMessage(*buffer.get());
+  } else {
+    if (queued_received_data_.size() > kMaxQueuedReceivedDataPackets) {
+      // TODO(jiayl): We should close the data channel in this case.
+      LOG(LS_ERROR)
+          << "Queued received data exceeds the max number of packes.";
+      ClearQueuedReceivedData();
+    }
+    queued_received_data_.push(buffer.release());
+  }
+}
+
+void DataChannel::OnChannelReady(bool writable) {
+  if (!writable) {
+    return;
+  }
+  // Update the readyState if the channel is writable for the first time;
+  // otherwise it means the channel was blocked for sending and now unblocked,
+  // so send the queued data now.
+  if (!was_ever_writable_) {
+    was_ever_writable_ = true;
+    UpdateState();
+  } else if (state_ == kOpen) {
+    SendQueuedSendData();
+  }
+}
+
 void DataChannel::DoClose() {
   receive_ssrc_set_ = false;
   send_ssrc_set_ = false;
@@ -201,7 +248,7 @@
           SetState(kOpen);
           // If we have received buffers before the channel got writable.
           // Deliver them now.
-          DeliverQueuedData();
+          DeliverQueuedReceivedData();
         }
       }
       break;
@@ -249,47 +296,76 @@
   data_session_ = NULL;
 }
 
-void DataChannel::DeliverQueuedData() {
-  if (was_ever_writable_ && observer_) {
-    while (!queued_data_.empty()) {
-      DataBuffer* buffer = queued_data_.front();
-      observer_->OnMessage(*buffer);
-      queued_data_.pop();
-      delete buffer;
-    }
+void DataChannel::DeliverQueuedReceivedData() {
+  if (!was_ever_writable_ || !observer_) {
+    return;
   }
-}
 
-void DataChannel::ClearQueuedData() {
-  while (!queued_data_.empty()) {
-    DataBuffer* buffer = queued_data_.front();
-    queued_data_.pop();
+  while (!queued_received_data_.empty()) {
+    DataBuffer* buffer = queued_received_data_.front();
+    observer_->OnMessage(*buffer);
+    queued_received_data_.pop();
     delete buffer;
   }
 }
 
-void DataChannel::OnDataReceived(cricket::DataChannel* channel,
-                                 const cricket::ReceiveDataParams& params,
-                                 const talk_base::Buffer& payload) {
-  if (params.ssrc == receive_ssrc_) {
-    bool binary = false;
-    talk_base::scoped_ptr<DataBuffer> buffer(new DataBuffer(payload, binary));
-    if (was_ever_writable_ && observer_) {
-      observer_->OnMessage(*buffer.get());
-    } else {
-      if (queued_data_.size() > kMaxQueuedDataPackets) {
-        ClearQueuedData();
-      }
-      queued_data_.push(buffer.release());
-    }
+void DataChannel::ClearQueuedReceivedData() {
+  while (!queued_received_data_.empty()) {
+    DataBuffer* buffer = queued_received_data_.front();
+    queued_received_data_.pop();
+    delete buffer;
   }
 }
 
-void DataChannel::OnChannelReady(bool writable) {
-  if (!was_ever_writable_ && writable) {
-    was_ever_writable_ = true;
-    UpdateState();
+void DataChannel::SendQueuedSendData() {
+  if (!was_ever_writable_) {
+    return;
   }
+
+  while (!queued_send_data_.empty()) {
+    DataBuffer* buffer = queued_send_data_.front();
+    cricket::SendDataResult send_result;
+    if (!InternalSendWithoutQueueing(*buffer, &send_result)) {
+      LOG(LS_WARNING) << "SendQueuedSendData aborted due to send_result "
+                      << send_result;
+      break;
+    }
+    queued_send_data_.pop_front();
+    delete buffer;
+  }
+}
+
+void DataChannel::ClearQueuedSendData() {
+  while (!queued_received_data_.empty()) {
+    DataBuffer* buffer = queued_received_data_.front();
+    queued_received_data_.pop();
+    delete buffer;
+  }
+}
+
+bool DataChannel::InternalSendWithoutQueueing(
+    const DataBuffer& buffer, cricket::SendDataResult* send_result) {
+  cricket::SendDataParams send_params;
+
+  send_params.ssrc = send_ssrc_;
+  if (session_->data_channel_type() == cricket::DCT_SCTP) {
+    send_params.ordered = config_.ordered;
+    send_params.max_rtx_count = config_.maxRetransmits;
+    send_params.max_rtx_ms = config_.maxRetransmitTime;
+  }
+  send_params.type = buffer.binary ? cricket::DMT_BINARY : cricket::DMT_TEXT;
+
+  return session_->data_channel()->SendData(send_params, buffer.data,
+                                            send_result);
+}
+
+bool DataChannel::QueueSendData(const DataBuffer& buffer) {
+  if (queued_send_data_.size() > kMaxQueuedSendDataPackets) {
+    LOG(LS_ERROR) << "Can't buffer any more data in the data channel.";
+    return false;
+  }
+  queued_send_data_.push_back(new DataBuffer(buffer));
+  return true;
 }
 
 }  // namespace webrtc
diff --git a/app/webrtc/datachannel.h b/app/webrtc/datachannel.h
index c79c491..440ee15 100644
--- a/app/webrtc/datachannel.h
+++ b/app/webrtc/datachannel.h
@@ -109,8 +109,13 @@
   void ConnectToDataSession();
   void DisconnectFromDataSession();
   bool IsConnectedToDataSession() { return data_session_ != NULL; }
-  void DeliverQueuedData();
-  void ClearQueuedData();
+  void DeliverQueuedReceivedData();
+  void ClearQueuedReceivedData();
+  void SendQueuedSendData();
+  void ClearQueuedSendData();
+  bool InternalSendWithoutQueueing(const DataBuffer& buffer,
+                                   cricket::SendDataResult* send_result);
+  bool QueueSendData(const DataBuffer& buffer);
 
   std::string label_;
   DataChannelInit config_;
@@ -123,7 +128,8 @@
   uint32 send_ssrc_;
   bool receive_ssrc_set_;
   uint32 receive_ssrc_;
-  std::queue<DataBuffer*> queued_data_;
+  std::queue<DataBuffer*> queued_received_data_;
+  std::deque<DataBuffer*> queued_send_data_;
 };
 
 class DataChannelFactory {
diff --git a/app/webrtc/datachannel_unittest.cc b/app/webrtc/datachannel_unittest.cc
new file mode 100644
index 0000000..d3faf17
--- /dev/null
+++ b/app/webrtc/datachannel_unittest.cc
@@ -0,0 +1,129 @@
+/*
+ * libjingle
+ * Copyright 2013, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/app/webrtc/datachannel.h"
+#include "talk/app/webrtc/mediastreamsignaling.h"
+#include "talk/app/webrtc/test/fakeconstraints.h"
+#include "talk/app/webrtc/webrtcsession.h"
+#include "talk/base/gunit.h"
+#include "talk/media/base/fakemediaengine.h"
+#include "talk/media/devices/fakedevicemanager.h"
+#include "talk/session/media/channelmanager.h"
+
+using webrtc::MediaConstraintsInterface;
+
+const uint32 kFakeSsrc = 1;
+
+class SctpDataChannelTest : public testing::Test {
+ protected:
+  SctpDataChannelTest()
+      : media_engine_(new cricket::FakeMediaEngine),
+        data_engine_(new cricket::FakeDataEngine),
+        channel_manager_(
+            new cricket::ChannelManager(media_engine_,
+                                        data_engine_,
+                                        new cricket::FakeDeviceManager(),
+                                        new cricket::CaptureManager(),
+                                        talk_base::Thread::Current())),
+        session_(channel_manager_.get(),
+                 talk_base::Thread::Current(),
+                 talk_base::Thread::Current(),
+                 NULL,
+                 new webrtc::MediaStreamSignaling(talk_base::Thread::Current(),
+                                                  NULL)),
+        webrtc_data_channel_(NULL) {}
+
+  virtual void SetUp() {
+    if (!talk_base::SSLStreamAdapter::HaveDtlsSrtp()) {
+      return;
+    }
+    channel_manager_->Init();
+    webrtc::FakeConstraints constraints;
+    constraints.AddMandatory(MediaConstraintsInterface::kEnableDtlsSrtp, true);
+    constraints.AddMandatory(MediaConstraintsInterface::kEnableSctpDataChannels,
+                             true);
+    ASSERT_TRUE(session_.Initialize(&constraints));
+    webrtc::SessionDescriptionInterface* offer = session_.CreateOffer(NULL);
+    ASSERT_TRUE(offer != NULL);
+    ASSERT_TRUE(session_.SetLocalDescription(offer, NULL));
+
+    webrtc_data_channel_ = webrtc::DataChannel::Create(&session_, "test", NULL);
+    // Connect to the media channel.
+    webrtc_data_channel_->SetSendSsrc(kFakeSsrc);
+    webrtc_data_channel_->SetReceiveSsrc(kFakeSsrc);
+
+    session_.data_channel()->SignalReadyToSendData(true);
+  }
+
+  void SetSendBlocked(bool blocked) {
+    bool was_blocked = data_engine_->GetChannel(0)->is_send_blocked();
+    data_engine_->GetChannel(0)->set_send_blocked(blocked);
+    if (!blocked && was_blocked) {
+      session_.data_channel()->SignalReadyToSendData(true);
+    }
+  }
+
+  cricket::FakeMediaEngine* media_engine_;
+  cricket::FakeDataEngine* data_engine_;
+  talk_base::scoped_ptr<cricket::ChannelManager> channel_manager_;
+  webrtc::WebRtcSession session_;
+  talk_base::scoped_refptr<webrtc::DataChannel> webrtc_data_channel_;
+};
+
+// Tests that DataChannel::buffered_amount() is correct after the channel is
+// blocked.
+TEST_F(SctpDataChannelTest, BufferedAmountWhenBlocked) {
+  if (!talk_base::SSLStreamAdapter::HaveDtlsSrtp()) {
+    return;
+  }
+  webrtc::DataBuffer buffer("abcd");
+  EXPECT_TRUE(webrtc_data_channel_->Send(buffer));
+
+  EXPECT_EQ(0U, webrtc_data_channel_->buffered_amount());
+
+  SetSendBlocked(true);
+  const int number_of_packets = 3;
+  for (int i = 0; i < number_of_packets; ++i) {
+    EXPECT_TRUE(webrtc_data_channel_->Send(buffer));
+  }
+  EXPECT_EQ(buffer.data.length() * number_of_packets,
+            webrtc_data_channel_->buffered_amount());
+}
+
+// Tests that the queued data are sent when the channel transitions from blocked
+// to unblocked.
+TEST_F(SctpDataChannelTest, QueuedDataSentWhenUnblocked) {
+  if (!talk_base::SSLStreamAdapter::HaveDtlsSrtp()) {
+    return;
+  }
+  webrtc::DataBuffer buffer("abcd");
+  SetSendBlocked(true);
+  EXPECT_TRUE(webrtc_data_channel_->Send(buffer));
+
+  SetSendBlocked(false);
+  EXPECT_EQ(0U, webrtc_data_channel_->buffered_amount());
+}
diff --git a/app/webrtc/datachannelinterface.h b/app/webrtc/datachannelinterface.h
index 6054e1b..82d375c 100644
--- a/app/webrtc/datachannelinterface.h
+++ b/app/webrtc/datachannelinterface.h
@@ -75,6 +75,8 @@
       : data(text.data(), text.length()),
         binary(false) {
   }
+  size_t size() const { return data.length(); }
+
   talk_base::Buffer data;
   // Indicates if the received data contains UTF-8 or binary data.
   // Note that the upper layers are left to verify the UTF-8 encoding.
diff --git a/app/webrtc/java/jni/peerconnection_jni.cc b/app/webrtc/java/jni/peerconnection_jni.cc
index d6a8b58..1d3c9cd 100644
--- a/app/webrtc/java/jni/peerconnection_jni.cc
+++ b/app/webrtc/java/jni/peerconnection_jni.cc
@@ -68,7 +68,7 @@
 #include "talk/media/base/videorenderer.h"
 #include "talk/media/devices/videorendererfactory.h"
 #include "talk/media/webrtc/webrtcvideocapturer.h"
-#include "third_party/icu/public/common/unicode/unistr.h"
+#include "third_party/icu/source/common/unicode/unistr.h"
 #include "webrtc/system_wrappers/interface/trace.h"
 #include "webrtc/video_engine/include/vie_base.h"
 #include "webrtc/voice_engine/include/voe_base.h"
diff --git a/app/webrtc/javatests/src/org/webrtc/PeerConnectionTest.java b/app/webrtc/javatests/src/org/webrtc/PeerConnectionTest.java
index 5d14ee5..542609d 100644
--- a/app/webrtc/javatests/src/org/webrtc/PeerConnectionTest.java
+++ b/app/webrtc/javatests/src/org/webrtc/PeerConnectionTest.java
@@ -57,8 +57,9 @@
     private final String name;
     private int expectedIceCandidates = 0;
     private int expectedErrors = 0;
-    private LinkedList<Integer> expectedSetSizeDimensions =
-        new LinkedList<Integer>();  // Alternating width/height.
+    private int expectedSetSize = 0;
+    private int previouslySeenWidth = 0;
+    private int previouslySeenHeight = 0;
     private int expectedFramesDelivered = 0;
     private LinkedList<SignalingState> expectedSignalingChanges =
         new LinkedList<SignalingState>();
@@ -116,21 +117,30 @@
       assertTrue(--expectedErrors >= 0);
     }
 
-    public synchronized void expectSetSize(int width, int height) {
+    public synchronized void expectSetSize() {
       if (RENDER_TO_GUI) {
         // When new frames are delivered to the GUI renderer we don't get
         // notified of frame size info.
         return;
       }
-      expectedSetSizeDimensions.add(width);
-      expectedSetSizeDimensions.add(height);
+      ++expectedSetSize;
     }
 
     @Override
     public synchronized void setSize(int width, int height) {
       assertFalse(RENDER_TO_GUI);
-      assertEquals(width, expectedSetSizeDimensions.removeFirst().intValue());
-      assertEquals(height, expectedSetSizeDimensions.removeFirst().intValue());
+      assertTrue(--expectedSetSize >= 0);
+      // Because different camera devices (fake & physical) produce different
+      // resolutions, we only sanity-check the set sizes,
+      assertTrue(width > 0);
+      assertTrue(height > 0);
+      if (previouslySeenWidth > 0) {
+        assertEquals(previouslySeenWidth, width);
+        assertEquals(previouslySeenHeight, height);
+      } else {
+        previouslySeenWidth = width;
+        previouslySeenHeight = height;
+      }
     }
 
     public synchronized void expectFramesDelivered(int count) {
@@ -292,9 +302,8 @@
         stillWaitingForExpectations.add(
             "expectedRemoveStreamLabels: " + expectedRemoveStreamLabels.size());
       }
-      if (!expectedSetSizeDimensions.isEmpty()) {
-        stillWaitingForExpectations.add(
-            "expectedSetSizeDimensions: " + expectedSetSizeDimensions.size());
+      if (expectedSetSize != 0) {
+        stillWaitingForExpectations.add("expectedSetSize");
       }
       if (expectedFramesDelivered > 0) {
         stillWaitingForExpectations.add(
@@ -506,7 +515,7 @@
     // serialized SDP, because the C++ API doesn't auto-translate.
     // Drop |label| params from {Audio,Video}Track-related APIs once
     // https://code.google.com/p/webrtc/issues/detail?id=1253 is fixed.
-    offeringExpectations.expectSetSize(640, 480);
+    offeringExpectations.expectSetSize();
     WeakReference<MediaStream> oLMS = addTracksToPC(
         factory, offeringPC, videoSource, "oLMS", "oLMSv0", "oLMSa0",
         offeringExpectations);
@@ -532,7 +541,7 @@
     assertTrue(sdpLatch.await());
     assertNull(sdpLatch.getSdp());
 
-    answeringExpectations.expectSetSize(640, 480);
+    answeringExpectations.expectSetSize();
     WeakReference<MediaStream> aLMS = addTracksToPC(
         factory, answeringPC, videoSource, "aLMS", "aLMSv0", "aLMSa0",
         answeringExpectations);
@@ -581,8 +590,8 @@
       // chosen arbitrarily).
       offeringExpectations.expectFramesDelivered(10);
       answeringExpectations.expectFramesDelivered(10);
-      offeringExpectations.expectSetSize(640, 480);
-      answeringExpectations.expectSetSize(640, 480);
+      offeringExpectations.expectSetSize();
+      answeringExpectations.expectSetSize();
     }
 
     offeringExpectations.expectIceConnectionChange(
diff --git a/base/host.cc b/base/host.cc
deleted file mode 100644
index 7decc49..0000000
--- a/base/host.cc
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * libjingle
- * Copyright 2004--2005, Google Inc.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- *  1. Redistributions of source code must retain the above copyright notice,
- *     this list of conditions and the following disclaimer.
- *  2. Redistributions in binary form must reproduce the above copyright notice,
- *     this list of conditions and the following disclaimer in the documentation
- *     and/or other materials provided with the distribution.
- *  3. The name of the author may not be used to endorse or promote products
- *     derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
- * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "talk/base/host.h"
-
-#ifdef POSIX
-#include <sys/utsname.h>
-#endif  // POSIX
-
-#include <string>
-
-namespace talk_base {
-
-std::string GetHostName() {
-  // TODO: fix or get rid of this
-#if 0
-  struct utsname nm;
-  if (uname(&nm) < 0)
-    FatalError("uname", LAST_SYSTEM_ERROR);
-  return std::string(nm.nodename);
-#endif
-  return "cricket";
-}
-
-}  // namespace talk_base
diff --git a/base/host.h b/base/host.h
deleted file mode 100644
index 8528240..0000000
--- a/base/host.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * libjingle
- * Copyright 2004--2005, Google Inc.
- *
- * Redistribution and use in source and binary forms, with or without 
- * modification, are permitted provided that the following conditions are met:
- *
- *  1. Redistributions of source code must retain the above copyright notice, 
- *     this list of conditions and the following disclaimer.
- *  2. Redistributions in binary form must reproduce the above copyright notice,
- *     this list of conditions and the following disclaimer in the documentation
- *     and/or other materials provided with the distribution.
- *  3. The name of the author may not be used to endorse or promote products 
- *     derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
- * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef TALK_BASE_HOST_H_
-#define TALK_BASE_HOST_H_
-
-#include <string>
-
-namespace talk_base {
-
-// Returns the name of the local host.
-std::string GetHostName();
-
-} // namespace talk_base
-
-#endif // TALK_BASE_HOST_H_
diff --git a/base/host_unittest.cc b/base/host_unittest.cc
deleted file mode 100644
index aba87af..0000000
--- a/base/host_unittest.cc
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * libjingle
- * Copyright 2004--2011, Google Inc.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- *  1. Redistributions of source code must retain the above copyright notice,
- *     this list of conditions and the following disclaimer.
- *  2. Redistributions in binary form must reproduce the above copyright notice,
- *     this list of conditions and the following disclaimer in the documentation
- *     and/or other materials provided with the distribution.
- *  3. The name of the author may not be used to endorse or promote products
- *     derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
- * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "talk/base/gunit.h"
-#include "talk/base/host.h"
-
-TEST(Host, GetHostName) {
-  EXPECT_NE("", talk_base::GetHostName());
-}
diff --git a/base/httpcommon.cc b/base/httpcommon.cc
index 458f2f9..ec7ffd2 100644
--- a/base/httpcommon.cc
+++ b/base/httpcommon.cc
@@ -528,11 +528,14 @@
 
 HttpError
 HttpRequestData::parseLeader(const char* line, size_t len) {
-  UNUSED(len);
   unsigned int vmajor, vminor;
   int vend, dstart, dend;
-  if ((sscanf(line, "%*s%n %n%*s%n HTTP/%u.%u", &vend, &dstart, &dend,
-              &vmajor, &vminor) != 2)
+  // sscanf isn't safe with strings that aren't null-terminated, and there is
+  // no guarantee that |line| is. Create a local copy that is null-terminated.
+  std::string line_str(line, len);
+  line = line_str.c_str();
+  if ((sscanf(line, "%*s%n %n%*s%n HTTP/%u.%u",
+              &vend, &dstart, &dend, &vmajor, &vminor) != 2)
       || (vmajor != 1)) {
     return HE_PROTOCOL;
   }
@@ -649,6 +652,10 @@
   size_t pos = 0;
   unsigned int vmajor, vminor, temp_scode;
   int temp_pos;
+  // sscanf isn't safe with strings that aren't null-terminated, and there is
+  // no guarantee that |line| is. Create a local copy that is null-terminated.
+  std::string line_str(line, len);
+  line = line_str.c_str();
   if (sscanf(line, "HTTP %u%n",
              &temp_scode, &temp_pos) == 1) {
     // This server's response has no version. :( NOTE: This happens for every
diff --git a/base/nat_unittest.cc b/base/nat_unittest.cc
index 03170ca..9771235 100644
--- a/base/nat_unittest.cc
+++ b/base/nat_unittest.cc
@@ -28,7 +28,6 @@
 #include <string>
 
 #include "talk/base/gunit.h"
-#include "talk/base/host.h"
 #include "talk/base/logging.h"
 #include "talk/base/natserver.h"
 #include "talk/base/natsocketfactory.h"
diff --git a/base/network.cc b/base/network.cc
index 9351b87..d6367c3 100644
--- a/base/network.cc
+++ b/base/network.cc
@@ -53,7 +53,6 @@
 #include <algorithm>
 #include <cstdio>
 
-#include "talk/base/host.h"
 #include "talk/base/logging.h"
 #include "talk/base/scoped_ptr.h"
 #include "talk/base/socket.h"  // includes something that makes windows happy
@@ -174,8 +173,7 @@
 }
 
 BasicNetworkManager::BasicNetworkManager()
-    : thread_(NULL),
-      start_count_(0) {
+    : thread_(NULL), sent_first_update_(false), start_count_(0) {
 }
 
 BasicNetworkManager::~BasicNetworkManager() {
diff --git a/base/testclient_unittest.cc b/base/testclient_unittest.cc
index 1269236..c1411f0 100644
--- a/base/testclient_unittest.cc
+++ b/base/testclient_unittest.cc
@@ -26,7 +26,6 @@
  */
 
 #include "talk/base/gunit.h"
-#include "talk/base/host.h"
 #include "talk/base/nethelpers.h"
 #include "talk/base/physicalsocketserver.h"
 #include "talk/base/testclient.h"
diff --git a/base/thread_unittest.cc b/base/thread_unittest.cc
index 11b493d..246faa4 100644
--- a/base/thread_unittest.cc
+++ b/base/thread_unittest.cc
@@ -28,7 +28,6 @@
 #include "talk/base/asyncudpsocket.h"
 #include "talk/base/event.h"
 #include "talk/base/gunit.h"
-#include "talk/base/host.h"
 #include "talk/base/physicalsocketserver.h"
 #include "talk/base/socketaddress.h"
 #include "talk/base/thread.h"
diff --git a/examples/android/assets/channel.html b/examples/android/assets/channel.html
index 86c2b44..d593442 100644
--- a/examples/android/assets/channel.html
+++ b/examples/android/assets/channel.html
@@ -5,39 +5,19 @@
   <!--
   Helper HTML that redirects Google AppEngine's Channel API to a JS object named
   |androidMessageHandler|, which is expected to be injected into the WebView
-  rendering this page by an Android app's class such as AppRTCClient
+  rendering this page by an Android app's class such as AppRTCClient.
   -->
   <body onbeforeunload="closeSocket()" onload="openSocket()">
     <script type="text/javascript">
-      // QueryString is copy/pasta from
-      // chromium's chrome/test/data/media/html/utils.js.
-      var QueryString = function () {
-        // Allows access to query parameters on the URL; e.g., given a URL like:
-        //    http://<url>/my.html?test=123&bob=123
-        // parameters can now be accessed via QueryString.test or QueryString.bob.
-        var params = {};
-
-        // RegEx to split out values by &.
-        var r = /([^&=]+)=?([^&]*)/g;
-
-        // Lambda function for decoding extracted match values. Replaces '+' with
-        // space so decodeURIComponent functions properly.
-        function d(s) { return decodeURIComponent(s.replace(/\+/g, ' ')); }
-
-        var match;
-        while (match = r.exec(window.location.search.substring(1)))
-          params[d(match[1])] = d(match[2]);
-
-        return params;
-      } ();
+      var token = androidMessageHandler.getToken();
+      if (!token)
+        throw "Missing/malformed token parameter: [" + token + "]";
 
       var channel = null;
       var socket = null;
 
       function openSocket() {
-        if (!QueryString.token || !QueryString.token.match(/^[A-z0-9_-]+$/))
-          throw "Missing/malformed token parameter: " + QueryString.token;
-        channel = new goog.appengine.Channel(QueryString.token);
+        channel = new goog.appengine.Channel(token);
         socket = channel.open({
           'onopen': function() { androidMessageHandler.onOpen(); },
           'onmessage': function(msg) { androidMessageHandler.onMessage(msg.data); },
diff --git a/examples/android/src/org/appspot/apprtc/GAEChannelClient.java b/examples/android/src/org/appspot/apprtc/GAEChannelClient.java
index 46f638d..29a9113 100644
--- a/examples/android/src/org/appspot/apprtc/GAEChannelClient.java
+++ b/examples/android/src/org/appspot/apprtc/GAEChannelClient.java
@@ -83,10 +83,11 @@
               ", desc: " + description);
         }
       });
-    proxyingMessageHandler = new ProxyingMessageHandler(activity, handler);
+    proxyingMessageHandler =
+        new ProxyingMessageHandler(activity, handler, token);
     webView.addJavascriptInterface(
         proxyingMessageHandler, "androidMessageHandler");
-    webView.loadUrl("file:///android_asset/channel.html?token=" + token);
+    webView.loadUrl("file:///android_asset/channel.html");
   }
 
   /** Close the connection to the AppEngine channel. */
@@ -106,10 +107,14 @@
     private final Activity activity;
     private final MessageHandler handler;
     private final boolean[] disconnected = { false };
+    private final String token;
 
-    public ProxyingMessageHandler(Activity activity, MessageHandler handler) {
+    public
+     ProxyingMessageHandler(Activity activity, MessageHandler handler,
+                            String token) {
       this.activity = activity;
       this.handler = handler;
+      this.token = token;
     }
 
     public void disconnect() {
@@ -120,6 +125,10 @@
       return disconnected[0];
     }
 
+    @JavascriptInterface public String getToken() {
+      return token;
+    }
+
     @JavascriptInterface public void onOpen() {
       activity.runOnUiThread(new Runnable() {
           public void run() {
diff --git a/libjingle.gyp b/libjingle.gyp
index 9207d0e..0944f76 100755
--- a/libjingle.gyp
+++ b/libjingle.gyp
@@ -300,8 +300,6 @@
         'base/gunit_prod.h',
         'base/helpers.cc',
         'base/helpers.h',
-        'base/host.cc',
-        'base/host.h',
         'base/httpbase.cc',
         'base/httpbase.h',
         'base/httpclient.cc',
@@ -574,7 +572,6 @@
               '-lcrypto',
               '-ldl',
               '-lrt',
-              '-lssl',
               '-lXext',
               '-lX11',
               '-lXcomposite',
@@ -650,7 +647,7 @@
             ],
           },
           'defines': [
-	    'SSL_USE_NSS',
+            'SSL_USE_NSS',
           ],
         }],
         ['OS=="win"', {
diff --git a/libjingle_examples.gyp b/libjingle_examples.gyp
index 192ba03..9170ba0 100755
--- a/libjingle_examples.gyp
+++ b/libjingle_examples.gyp
@@ -238,6 +238,7 @@
                 'examples/android/AndroidManifest.xml',
                 'examples/android/README',
                 'examples/android/ant.properties',
+                'examples/android/assets/channel.html',
                 'examples/android/build.xml',
                 'examples/android/jni/Android.mk',
                 'examples/android/project.properties',
diff --git a/libjingle_tests.gyp b/libjingle_tests.gyp
index 4b7b793..aba09ed 100755
--- a/libjingle_tests.gyp
+++ b/libjingle_tests.gyp
@@ -121,7 +121,6 @@
         'base/filelock_unittest.cc',
         'base/fileutils_unittest.cc',
         'base/helpers_unittest.cc',
-        'base/host_unittest.cc',
         'base/httpbase_unittest.cc',
         'base/httpcommon_unittest.cc',
         'base/httpserver_unittest.cc',
@@ -376,6 +375,7 @@
       ],
       # TODO(ronghuawu): Reenable below unit tests that require gmock.
       'sources': [
+        'app/webrtc/datachannel_unittest.cc',
         'app/webrtc/dtmfsender_unittest.cc',
         'app/webrtc/jsepsessiondescription_unittest.cc',
         'app/webrtc/localaudiosource_unittest.cc',
@@ -402,7 +402,7 @@
     },  # target libjingle_peerconnection_unittest
   ],
   'conditions': [
-    ['OS=="linux"', {
+    ['OS=="linux" or OS=="android"', {
       'targets': [
         {
           'target_name': 'libjingle_peerconnection_test_jar',
@@ -426,7 +426,7 @@
                 '<(PRODUCT_DIR)/libjingle_peerconnection_test.jar',
               ],
               'action': [
-                'build/build_jar.sh', '/usr', '<@(_outputs)',
+                'build/build_jar.sh', '<(java_home)', '<@(_outputs)',
                 '<(INTERMEDIATE_DIR)',
                 '<(java_src_dir):<(PRODUCT_DIR)/libjingle_peerconnection.jar:<(DEPTH)/third_party/junit/junit-4.11.jar',
                 '<@(java_files)'
diff --git a/media/base/capturemanager.cc b/media/base/capturemanager.cc
index 5705bcd..85bfa54 100644
--- a/media/base/capturemanager.cc
+++ b/media/base/capturemanager.cc
@@ -145,12 +145,19 @@
 }
 
 CaptureManager::~CaptureManager() {
-  while (!capture_states_.empty()) {
-    // There may have been multiple calls to StartVideoCapture which means that
-    // an equal number of calls to StopVideoCapture must be made. Note that
-    // StopVideoCapture will remove the element from |capture_states_| when a
-    // successfull stop has been made.
-    UnregisterVideoCapturer(capture_states_.begin()->second);
+  // Since we don't own any of the capturers, all capturers should have been
+  // cleaned up before we get here. In fact, in the normal shutdown sequence,
+  // all capturers *will* be shut down by now, so trying to stop them here
+  // will crash. If we're still tracking any, it's a dangling pointer.
+  if (!capture_states_.empty()) {
+    ASSERT(false &&
+           "CaptureManager destructing while still tracking capturers!");
+    // Delete remaining VideoCapturerStates, but don't touch the capturers.
+    do {
+      CaptureStates::iterator it = capture_states_.begin();
+      delete it->second;
+      capture_states_.erase(it);
+    } while (!capture_states_.empty());
   }
 }
 
diff --git a/media/base/capturemanager_unittest.cc b/media/base/capturemanager_unittest.cc
index e9d9cfb..8025e56 100644
--- a/media/base/capturemanager_unittest.cc
+++ b/media/base/capturemanager_unittest.cc
@@ -180,6 +180,10 @@
   EXPECT_TRUE(video_capturer_.CaptureFrame());
   EXPECT_EQ(1, NumFramesRendered());
   EXPECT_TRUE(WasRenderedResolution(format_qvga_));
+  EXPECT_TRUE(capture_manager_.StopVideoCapture(&video_capturer_,
+                                                format_qvga_));
+  EXPECT_TRUE(capture_manager_.StopVideoCapture(&video_capturer_,
+                                                format_vga_));
 }
 
 // Ensure that the reference counting is working when multiple start and
@@ -230,6 +234,8 @@
   EXPECT_TRUE(video_capturer_.CaptureFrame());
   EXPECT_EQ(2, NumFramesRendered());
   EXPECT_TRUE(WasRenderedResolution(format_vga_));
+  EXPECT_TRUE(capture_manager_.StopVideoCapture(&video_capturer_,
+                                                format_vga_));
 }
 
 TEST_F(CaptureManagerTest, TestRequestRestart) {
@@ -248,4 +254,6 @@
   EXPECT_TRUE(video_capturer_.CaptureFrame());
   EXPECT_EQ(2, NumFramesRendered());
   EXPECT_TRUE(WasRenderedResolution(format_vga_));
+  EXPECT_TRUE(capture_manager_.StopVideoCapture(&video_capturer_,
+                                                format_qvga_));
 }
diff --git a/media/base/fakemediaengine.h b/media/base/fakemediaengine.h
index 7f4c9e3..ded5698 100644
--- a/media/base/fakemediaengine.h
+++ b/media/base/fakemediaengine.h
@@ -599,7 +599,7 @@
 class FakeDataMediaChannel : public RtpHelper<DataMediaChannel> {
  public:
   explicit FakeDataMediaChannel(void* unused)
-      : auto_bandwidth_(false), max_bps_(-1) {}
+      : auto_bandwidth_(false), send_blocked_(false), max_bps_(-1) {}
   ~FakeDataMediaChannel() {}
   const std::vector<DataCodec>& recv_codecs() const { return recv_codecs_; }
   const std::vector<DataCodec>& send_codecs() const { return send_codecs_; }
@@ -647,13 +647,20 @@
   virtual bool SendData(const SendDataParams& params,
                         const talk_base::Buffer& payload,
                         SendDataResult* result) {
-    last_sent_data_params_ = params;
-    last_sent_data_ = std::string(payload.data(), payload.length());
-    return true;
+    if (send_blocked_) {
+      *result = SDR_BLOCK;
+      return false;
+    } else {
+      last_sent_data_params_ = params;
+      last_sent_data_ = std::string(payload.data(), payload.length());
+      return true;
+    }
   }
 
   SendDataParams last_sent_data_params() { return last_sent_data_params_; }
   std::string last_sent_data() { return last_sent_data_; }
+  bool is_send_blocked() { return send_blocked_; }
+  void set_send_blocked(bool blocked) { send_blocked_ = blocked; }
 
  private:
   std::vector<DataCodec> recv_codecs_;
@@ -661,6 +668,7 @@
   SendDataParams last_sent_data_params_;
   std::string last_sent_data_;
   bool auto_bandwidth_;
+  bool send_blocked_;
   int max_bps_;
 };
 
diff --git a/media/base/mediachannel.h b/media/base/mediachannel.h
index 07d5f90..a9e3778 100644
--- a/media/base/mediachannel.h
+++ b/media/base/mediachannel.h
@@ -957,6 +957,9 @@
   // Signal errors from MediaChannel.  Arguments are:
   //     ssrc(uint32), and error(DataMediaChannel::Error).
   sigslot::signal2<uint32, DataMediaChannel::Error> SignalMediaError;
+  // Signal when the media channel is ready to send the stream. Arguments are:
+  //     writable(bool)
+  sigslot::signal1<bool> SignalReadyToSend;
 };
 
 }  // namespace cricket
diff --git a/media/sctp/sctpdataengine.cc b/media/sctp/sctpdataengine.cc
index 7fcb239..71ef73c 100644
--- a/media/sctp/sctpdataengine.cc
+++ b/media/sctp/sctpdataengine.cc
@@ -270,7 +270,8 @@
   // Subscribe to SCTP event notifications.
   int event_types[] = {SCTP_ASSOC_CHANGE,
                        SCTP_PEER_ADDR_CHANGE,
-                       SCTP_SEND_FAILED_EVENT};
+                       SCTP_SEND_FAILED_EVENT,
+                       SCTP_SENDER_DRY_EVENT};
   struct sctp_event event = {0};
   event.se_assoc_id = SCTP_ALL_ASSOC;
   event.se_on = 1;
@@ -479,11 +480,14 @@
                               static_cast<socklen_t>(sizeof(sndinfo)),
                               SCTP_SENDV_SNDINFO, 0);
   if (res < 0) {
-    LOG_ERRNO(LS_ERROR) << "ERROR:" << debug_name_
-                        << "SendData->(...): "
-                        << " usrsctp_sendv: ";
-    // TODO(pthatcher): Make result SDR_BLOCK if the error is because
-    // it would block.
+    if (errno == EWOULDBLOCK) {
+      *result = SDR_BLOCK;
+      LOG(LS_INFO) << debug_name_ << "->SendData(...): EWOULDBLOCK returned";
+    } else {
+      LOG_ERRNO(LS_ERROR) << "ERROR:" << debug_name_
+                          << "->SendData(...): "
+                          << " usrsctp_sendv: ";
+    }
     return false;
   }
   if (result) {
@@ -562,8 +566,7 @@
   }
 }
 
-void SctpDataMediaChannel::OnNotificationFromSctp(
-    talk_base::Buffer* buffer) {
+void SctpDataMediaChannel::OnNotificationFromSctp(talk_base::Buffer* buffer) {
   const sctp_notification& notification =
       reinterpret_cast<const sctp_notification&>(*buffer->data());
   ASSERT(notification.sn_header.sn_length == buffer->length());
@@ -591,6 +594,7 @@
       break;
     case SCTP_SENDER_DRY_EVENT:
       LOG(LS_INFO) << "SCTP_SENDER_DRY_EVENT";
+      SignalReadyToSend(true);
       break;
     // TODO(ldixon): Unblock after congestion.
     case SCTP_NOTIFICATIONS_STOPPED_EVENT:
diff --git a/media/sctp/sctpdataengine_unittest.cc b/media/sctp/sctpdataengine_unittest.cc
index 071fbbb..2b8787f 100644
--- a/media/sctp/sctpdataengine_unittest.cc
+++ b/media/sctp/sctpdataengine_unittest.cc
@@ -137,6 +137,24 @@
   cricket::ReceiveDataParams last_params_;
 };
 
+class SignalReadyToSendObserver : public sigslot::has_slots<> {
+ public:
+  SignalReadyToSendObserver() : signaled_(false), writable_(false) {}
+
+  void OnSignaled(bool writable) {
+    signaled_ = true;
+    writable_ = writable;
+  }
+
+  bool IsSignaled(bool writable) {
+    return signaled_ && (writable_ == writable);
+  }
+
+ private:
+  bool signaled_;
+  bool writable_;
+};
+
 // SCTP Data Engine testing framework.
 class SctpDataMediaChannelTest : public testing::Test {
  protected:
@@ -144,6 +162,42 @@
     engine_.reset(new cricket::SctpDataEngine());
   }
 
+  void SetupConnectedChannels() {
+    net1_.reset(new SctpFakeNetworkInterface(talk_base::Thread::Current()));
+    net2_.reset(new SctpFakeNetworkInterface(talk_base::Thread::Current()));
+    recv1_.reset(new SctpFakeDataReceiver());
+    recv2_.reset(new SctpFakeDataReceiver());
+    chan1_.reset(CreateChannel(net1_.get(), recv1_.get()));
+    chan1_->set_debug_name("chan1/connector");
+    chan2_.reset(CreateChannel(net2_.get(), recv2_.get()));
+    chan2_->set_debug_name("chan2/listener");
+    // Setup two connected channels ready to send and receive.
+    net1_->SetDestination(chan2_.get());
+    net2_->SetDestination(chan1_.get());
+
+    LOG(LS_VERBOSE) << "Channel setup ----------------------------- ";
+    chan1_->AddSendStream(cricket::StreamParams::CreateLegacy(1));
+    chan2_->AddRecvStream(cricket::StreamParams::CreateLegacy(1));
+
+    chan2_->AddSendStream(cricket::StreamParams::CreateLegacy(2));
+    chan1_->AddRecvStream(cricket::StreamParams::CreateLegacy(2));
+
+    LOG(LS_VERBOSE) << "Connect the channels -----------------------------";
+    // chan1 wants to setup a data connection.
+    chan1_->SetReceive(true);
+    // chan1 will have sent chan2 a request to setup a data connection. After
+    // chan2 accepts the offer, chan2 connects to chan1 with the following.
+    chan2_->SetReceive(true);
+    chan2_->SetSend(true);
+    // Makes sure that network packets are delivered and simulates a
+    // deterministic and realistic small timing delay between the SetSend calls.
+    ProcessMessagesUntilIdle();
+
+    // chan1 and chan2 are now connected so chan1 enables sending to complete
+    // the creation of the connection.
+    chan1_->SetSend(true);
+  }
+
   cricket::SctpDataMediaChannel* CreateChannel(
         SctpFakeNetworkInterface* net, SctpFakeDataReceiver* recv) {
     cricket::SctpDataMediaChannel* channel =
@@ -182,79 +236,78 @@
     return !thread->IsQuitting();
   }
 
+  cricket::SctpDataMediaChannel* channel1() { return chan1_.get(); }
+  cricket::SctpDataMediaChannel* channel2() { return chan2_.get(); }
+  SctpFakeDataReceiver* receiver1() { return recv1_.get(); }
+  SctpFakeDataReceiver* receiver2() { return recv2_.get(); }
+
  private:
   talk_base::scoped_ptr<cricket::SctpDataEngine> engine_;
+  talk_base::scoped_ptr<SctpFakeNetworkInterface> net1_;
+  talk_base::scoped_ptr<SctpFakeNetworkInterface> net2_;
+  talk_base::scoped_ptr<SctpFakeDataReceiver> recv1_;
+  talk_base::scoped_ptr<SctpFakeDataReceiver> recv2_;
+  talk_base::scoped_ptr<cricket::SctpDataMediaChannel> chan1_;
+  talk_base::scoped_ptr<cricket::SctpDataMediaChannel> chan2_;
 };
 
+// Verifies that SignalReadyToSend is fired.
+TEST_F(SctpDataMediaChannelTest, SignalReadyToSend) {
+  SetupConnectedChannels();
+
+  SignalReadyToSendObserver signal_observer_1;
+  SignalReadyToSendObserver signal_observer_2;
+
+  channel1()->SignalReadyToSend.connect(&signal_observer_1,
+                                        &SignalReadyToSendObserver::OnSignaled);
+  channel2()->SignalReadyToSend.connect(&signal_observer_2,
+                                        &SignalReadyToSendObserver::OnSignaled);
+
+  cricket::SendDataResult result;
+  ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result));
+  EXPECT_EQ(cricket::SDR_SUCCESS, result);
+  EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000);
+  ASSERT_TRUE(SendData(channel2(), 2, "hi chan1", &result));
+  EXPECT_EQ(cricket::SDR_SUCCESS, result);
+  EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi chan1"), 1000);
+
+  EXPECT_TRUE_WAIT(signal_observer_1.IsSignaled(true), 1000);
+  EXPECT_TRUE_WAIT(signal_observer_2.IsSignaled(true), 1000);
+}
+
 TEST_F(SctpDataMediaChannelTest, SendData) {
-  talk_base::scoped_ptr<SctpFakeNetworkInterface> net1(
-      new SctpFakeNetworkInterface(talk_base::Thread::Current()));
-  talk_base::scoped_ptr<SctpFakeNetworkInterface> net2(
-      new SctpFakeNetworkInterface(talk_base::Thread::Current()));
-  talk_base::scoped_ptr<SctpFakeDataReceiver> recv1(
-      new SctpFakeDataReceiver());
-  talk_base::scoped_ptr<SctpFakeDataReceiver> recv2(
-      new SctpFakeDataReceiver());
-  talk_base::scoped_ptr<cricket::SctpDataMediaChannel> chan1(
-      CreateChannel(net1.get(), recv1.get()));
-  chan1->set_debug_name("chan1/connector");
-  talk_base::scoped_ptr<cricket::SctpDataMediaChannel> chan2(
-      CreateChannel(net2.get(), recv2.get()));
-  chan2->set_debug_name("chan2/listener");
-
-  net1->SetDestination(chan2.get());
-  net2->SetDestination(chan1.get());
-
-  LOG(LS_VERBOSE) << "Channel setup ----------------------------- ";
-  chan1->AddSendStream(cricket::StreamParams::CreateLegacy(1));
-  chan2->AddRecvStream(cricket::StreamParams::CreateLegacy(1));
-
-  chan2->AddSendStream(cricket::StreamParams::CreateLegacy(2));
-  chan1->AddRecvStream(cricket::StreamParams::CreateLegacy(2));
-
-  LOG(LS_VERBOSE) << "Connect the channels -----------------------------";
-  // chan1 wants to setup a data connection.
-  chan1->SetReceive(true);
-  // chan1 will have sent chan2 a request to setup a data connection. After
-  // chan2 accepts the offer, chan2 connects to chan1 with the following.
-  chan2->SetReceive(true);
-  chan2->SetSend(true);
-  // Makes sure that network packets are delivered and simulates a
-  // deterministic and realistic small timing delay between the SetSend calls.
-  ProcessMessagesUntilIdle();
-
-  // chan1 and chan2 are now connected so chan1 enables sending to complete
-  // the creation of the connection.
-  chan1->SetSend(true);
+  SetupConnectedChannels();
 
   cricket::SendDataResult result;
   LOG(LS_VERBOSE) << "chan1 sending: 'hello?' -----------------------------";
-  ASSERT_TRUE(SendData(chan1.get(), 1, "hello?", &result));
+  ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result));
   EXPECT_EQ(cricket::SDR_SUCCESS, result);
-  EXPECT_TRUE_WAIT(ReceivedData(recv2.get(), 1, "hello?"), 1000);
-  LOG(LS_VERBOSE) << "recv2.received=" << recv2->received()
-                  << "recv2.last_params.ssrc=" << recv2->last_params().ssrc
+  EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000);
+  LOG(LS_VERBOSE) << "recv2.received=" << receiver2()->received()
+                  << "recv2.last_params.ssrc="
+                  << receiver2()->last_params().ssrc
                   << "recv2.last_params.timestamp="
-                  << recv2->last_params().ssrc
+                  << receiver2()->last_params().ssrc
                   << "recv2.last_params.seq_num="
-                  << recv2->last_params().seq_num
-                  << "recv2.last_data=" << recv2->last_data();
+                  << receiver2()->last_params().seq_num
+                  << "recv2.last_data=" << receiver2()->last_data();
 
   LOG(LS_VERBOSE) << "chan2 sending: 'hi chan1' -----------------------------";
-  ASSERT_TRUE(SendData(chan2.get(), 2, "hi chan1", &result));
+  ASSERT_TRUE(SendData(channel2(), 2, "hi chan1", &result));
   EXPECT_EQ(cricket::SDR_SUCCESS, result);
-  EXPECT_TRUE_WAIT(ReceivedData(recv1.get(), 2, "hi chan1"), 1000);
-  LOG(LS_VERBOSE) << "recv1.received=" << recv1->received()
-                  << "recv1.last_params.ssrc=" << recv1->last_params().ssrc
+  EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi chan1"), 1000);
+  LOG(LS_VERBOSE) << "recv1.received=" << receiver1()->received()
+                  << "recv1.last_params.ssrc="
+                  << receiver1()->last_params().ssrc
                   << "recv1.last_params.timestamp="
-                  << recv1->last_params().ssrc
+                  << receiver1()->last_params().ssrc
                   << "recv1.last_params.seq_num="
-                  << recv1->last_params().seq_num
-                  << "recv1.last_data=" << recv1->last_data();
+                  << receiver1()->last_params().seq_num
+                  << "recv1.last_data=" << receiver1()->last_data();
 
   LOG(LS_VERBOSE) << "Closing down. -----------------------------";
   // Disconnects and closes socket, including setting receiving to false.
-  chan1->SetSend(false);
-  chan2->SetSend(false);
+  channel1()->SetSend(false);
+  channel2()->SetSend(false);
   LOG(LS_VERBOSE) << "Cleaning up. -----------------------------";
 }
diff --git a/media/webrtc/webrtcvideoengine.cc b/media/webrtc/webrtcvideoengine.cc
index 19ccebf..10cdd8e 100644
--- a/media/webrtc/webrtcvideoengine.cc
+++ b/media/webrtc/webrtcvideoengine.cc
@@ -429,7 +429,7 @@
   DecoderMap registered_decoders_;
 };
 
-class WebRtcVideoChannelSendInfo  {
+class WebRtcVideoChannelSendInfo : public sigslot::has_slots<> {
  public:
   typedef std::map<int, webrtc::VideoEncoder*> EncoderMap;  // key: payload type
   WebRtcVideoChannelSendInfo(int channel_id, int capture_id,
@@ -445,9 +445,7 @@
         capturer_updated_(false),
         interval_(0),
         video_adapter_(new CoordinatedVideoAdapter) {
-    // TODO(asapersson):
-    // video_adapter_->SignalCpuAdaptationUnable.connect(
-    //     this, &WebRtcVideoChannelSendInfo::OnCpuAdaptationUnable);
+    SignalCpuAdaptationUnable.repeat(video_adapter_->SignalCpuAdaptationUnable);
     if (cpu_monitor) {
       cpu_monitor->SignalUpdate.connect(
           video_adapter_.get(), &CoordinatedVideoAdapter::OnCpuLoadUpdated);
@@ -585,6 +583,8 @@
     registered_encoders_.clear();
   }
 
+  sigslot::repeater0<> SignalCpuAdaptationUnable;
+
  private:
   int channel_id_;
   int capture_id_;
@@ -2931,6 +2931,8 @@
                                      external_capture,
                                      engine()->cpu_monitor()));
   send_channel->ApplyCpuOptions(options_);
+  send_channel->SignalCpuAdaptationUnable.connect(this,
+      &WebRtcVideoMediaChannel::OnCpuAdaptationUnable);
 
   // Register encoder observer for outgoing framerate and bitrate.
   if (engine()->vie()->codec()->RegisterEncoderObserver(
@@ -3404,6 +3406,12 @@
   }
 }
 
+void WebRtcVideoMediaChannel::OnCpuAdaptationUnable() {
+  // ssrc is hardcoded to 0.  This message is based on a system wide issue,
+  // so finding which ssrc caused it doesn't matter.
+  SignalMediaError(0, VideoMediaChannel::ERROR_REC_CPU_MAX_CANT_DOWNGRADE);
+}
+
 void WebRtcVideoMediaChannel::SetNetworkTransmissionState(
     bool is_transmitting) {
   LOG(LS_INFO) << "SetNetworkTransmissionState: " << is_transmitting;
diff --git a/media/webrtc/webrtcvideoengine.h b/media/webrtc/webrtcvideoengine.h
index 2f0fd3e..f0293bb 100644
--- a/media/webrtc/webrtcvideoengine.h
+++ b/media/webrtc/webrtcvideoengine.h
@@ -396,6 +396,9 @@
                           const std::vector<RtpHeaderExtension>& extensions,
                           const char header_extension_uri[]);
 
+  // Signal when cpu adaptation has no further scope to adapt.
+  void OnCpuAdaptationUnable();
+
   // Global state.
   WebRtcVideoEngine* engine_;
   VoiceMediaChannel* voice_channel_;
diff --git a/media/webrtc/webrtcvideoengine_unittest.cc b/media/webrtc/webrtcvideoengine_unittest.cc
index 376f295..840fcdd 100644
--- a/media/webrtc/webrtcvideoengine_unittest.cc
+++ b/media/webrtc/webrtcvideoengine_unittest.cc
@@ -86,7 +86,9 @@
 
 // Test fixture to test WebRtcVideoEngine with a fake webrtc::VideoEngine.
 // Useful for testing failure paths.
-class WebRtcVideoEngineTestFake : public testing::Test {
+class WebRtcVideoEngineTestFake :
+  public testing::Test,
+  public sigslot::has_slots<> {
  public:
   WebRtcVideoEngineTestFake()
       : vie_(kVideoCodecs, ARRAY_SIZE(kVideoCodecs)),
@@ -95,16 +97,22 @@
         engine_(NULL,  // cricket::WebRtcVoiceEngine
                 new FakeViEWrapper(&vie_), cpu_monitor_),
         channel_(NULL),
-        voice_channel_(NULL) {
+        voice_channel_(NULL),
+        last_error_(cricket::VideoMediaChannel::ERROR_NONE) {
   }
   bool SetupEngine() {
     bool result = engine_.Init(talk_base::Thread::Current());
     if (result) {
       channel_ = engine_.CreateChannel(voice_channel_);
+      channel_->SignalMediaError.connect(this,
+          &WebRtcVideoEngineTestFake::OnMediaError);
       result = (channel_ != NULL);
     }
     return result;
   }
+  void OnMediaError(uint32 ssrc, cricket::VideoMediaChannel::Error error) {
+    last_error_ = error;
+  }
   bool SendI420Frame(int width, int height) {
     if (NULL == channel_) {
       return false;
@@ -185,6 +193,7 @@
   cricket::WebRtcVideoEngine engine_;
   cricket::WebRtcVideoMediaChannel* channel_;
   cricket::WebRtcVoiceMediaChannel* voice_channel_;
+  cricket::VideoMediaChannel::Error last_error_;
 };
 
 // Test fixtures to test WebRtcVideoEngine with a real webrtc::VideoEngine.
diff --git a/p2p/base/port_unittest.cc b/p2p/base/port_unittest.cc
index 1c6752b..2feeee2 100644
--- a/p2p/base/port_unittest.cc
+++ b/p2p/base/port_unittest.cc
@@ -28,7 +28,6 @@
 #include "talk/base/crc32.h"
 #include "talk/base/gunit.h"
 #include "talk/base/helpers.h"
-#include "talk/base/host.h"
 #include "talk/base/logging.h"
 #include "talk/base/natserver.h"
 #include "talk/base/natsocketfactory.h"
diff --git a/p2p/base/relayserver_unittest.cc b/p2p/base/relayserver_unittest.cc
index 7580e45..86d2eef 100644
--- a/p2p/base/relayserver_unittest.cc
+++ b/p2p/base/relayserver_unittest.cc
@@ -29,7 +29,6 @@
 
 #include "talk/base/gunit.h"
 #include "talk/base/helpers.h"
-#include "talk/base/host.h"
 #include "talk/base/logging.h"
 #include "talk/base/physicalsocketserver.h"
 #include "talk/base/socketaddress.h"
diff --git a/p2p/base/session_unittest.cc b/p2p/base/session_unittest.cc
index ae0c383..1d072ae 100644
--- a/p2p/base/session_unittest.cc
+++ b/p2p/base/session_unittest.cc
@@ -34,7 +34,6 @@
 #include "talk/base/common.h"
 #include "talk/base/gunit.h"
 #include "talk/base/helpers.h"
-#include "talk/base/host.h"
 #include "talk/base/logging.h"
 #include "talk/base/natserver.h"
 #include "talk/base/natsocketfactory.h"
diff --git a/p2p/base/stunserver_main.cc b/p2p/base/stunserver_main.cc
index e697728..4467944 100644
--- a/p2p/base/stunserver_main.cc
+++ b/p2p/base/stunserver_main.cc
@@ -31,7 +31,6 @@
 
 #include <iostream>
 
-#include "talk/base/host.h"
 #include "talk/base/thread.h"
 #include "talk/p2p/base/stunserver.h"
 
diff --git a/p2p/client/basicportallocator.cc b/p2p/client/basicportallocator.cc
index 7a61093..a728d98 100644
--- a/p2p/client/basicportallocator.cc
+++ b/p2p/client/basicportallocator.cc
@@ -32,7 +32,6 @@
 
 #include "talk/base/common.h"
 #include "talk/base/helpers.h"
-#include "talk/base/host.h"
 #include "talk/base/logging.h"
 #include "talk/p2p/base/basicpacketsocketfactory.h"
 #include "talk/p2p/base/common.h"
diff --git a/session/media/channel.cc b/session/media/channel.cc
index 948a02c..379c553 100644
--- a/session/media/channel.cc
+++ b/session/media/channel.cc
@@ -2294,6 +2294,7 @@
       SetScreenCaptureFactoryMessageData* data =
           static_cast<SetScreenCaptureFactoryMessageData*>(pmsg->pdata);
       SetScreenCaptureFactory_w(data->screencapture_factory);
+      break;
     }
     case MSG_GETSTATS: {
       VideoStatsMessageData* data =
@@ -2428,6 +2429,8 @@
       this, &DataChannel::OnDataReceived);
   media_channel()->SignalMediaError.connect(
       this, &DataChannel::OnDataChannelError);
+  media_channel()->SignalReadyToSend.connect(
+      this, &DataChannel::OnDataChannelReadyToSend);
   srtp_filter()->SignalSrtpError.connect(
       this, &DataChannel::OnSrtpError);
   return true;
@@ -2609,7 +2612,7 @@
 
   // Post to trigger SignalReadyToSendData.
   signaling_thread()->Post(this, MSG_READYTOSENDDATA,
-                           new BoolMessageData(send));
+                           new DataChannelReadyToSendMessageData(send));
 
   LOG(LS_INFO) << "Changing data state, recv=" << recv << " send=" << send;
 }
@@ -2617,7 +2620,8 @@
 void DataChannel::OnMessage(talk_base::Message *pmsg) {
   switch (pmsg->message_id) {
     case MSG_READYTOSENDDATA: {
-      BoolMessageData* data = static_cast<BoolMessageData*>(pmsg->pdata);
+      DataChannelReadyToSendMessageData* data =
+          static_cast<DataChannelReadyToSendMessageData*>(pmsg->pdata);
       SignalReadyToSendData(data->data());
       delete data;
       break;
@@ -2690,6 +2694,14 @@
   signaling_thread()->Post(this, MSG_CHANNEL_ERROR, data);
 }
 
+void DataChannel::OnDataChannelReadyToSend(bool writable) {
+  // This is usded for congestion control to indicate that the stream is ready
+  // to send by the MediaChannel, as opposed to OnReadyToSend, which indicates
+  // that the transport channel is ready.
+  signaling_thread()->Post(this, MSG_READYTOSENDDATA,
+                           new DataChannelReadyToSendMessageData(writable));
+}
+
 void DataChannel::OnSrtpError(uint32 ssrc, SrtpFilter::Mode mode,
                               SrtpFilter::Error error) {
   switch (error) {
diff --git a/session/media/channel.h b/session/media/channel.h
index 9bac965..eccadd3 100644
--- a/session/media/channel.h
+++ b/session/media/channel.h
@@ -595,9 +595,9 @@
     return static_cast<DataMediaChannel*>(BaseChannel::media_channel());
   }
 
-  bool SendData(const SendDataParams& params,
-                const talk_base::Buffer& payload,
-                SendDataResult* result);
+  virtual bool SendData(const SendDataParams& params,
+                        const talk_base::Buffer& payload,
+                        SendDataResult* result);
 
   void StartMediaMonitor(int cms);
   void StopMediaMonitor();
@@ -612,9 +612,8 @@
                    const talk_base::Buffer&>
       SignalDataReceived;
   // Signal for notifying when the channel becomes ready to send data.
-  // That occurs when the channel is enabled, the transport is writable and
-  // both local and remote descriptions are set.
-  // TODO(perkj): Signal this per SSRC stream.
+  // That occurs when the channel is enabled, the transport is writable,
+  // both local and remote descriptions are set, and the channel is unblocked.
   sigslot::signal1<bool> SignalReadyToSendData;
 
  private:
@@ -647,6 +646,8 @@
     const talk_base::Buffer payload;
   };
 
+  typedef talk_base::TypedMessageData<bool> DataChannelReadyToSendMessageData;
+
   // overrides from BaseChannel
   virtual const ContentInfo* GetFirstContent(const SessionDescription* sdesc);
   // If data_channel_type_ is DCT_NONE, set it.  Otherwise, check that
@@ -674,6 +675,7 @@
   void OnDataReceived(
       const ReceiveDataParams& params, const char* data, size_t len);
   void OnDataChannelError(uint32 ssrc, DataMediaChannel::Error error);
+  void OnDataChannelReadyToSend(bool writable);
   void OnSrtpError(uint32 ssrc, SrtpFilter::Mode mode, SrtpFilter::Error error);
 
   talk_base::scoped_ptr<DataMediaMonitor> media_monitor_;
diff --git a/session/media/channelmanager.h b/session/media/channelmanager.h
index e5e6e44..b1967bf 100644
--- a/session/media/channelmanager.h
+++ b/session/media/channelmanager.h
@@ -297,8 +297,6 @@
   bool capturing_;
   bool monitoring_;
 
-  talk_base::scoped_ptr<VideoCapturer> video_capturer_;
-
   // String containing currently set device. Note that this string is subtly
   // different from camera_device_. E.g. camera_device_ will list unplugged
   // but selected devices while this sting will be empty or contain current