Update stable to r5195.

git-svn-id: http://webrtc.googlecode.com/svn/stable/talk@5196 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/app/webrtc/peerconnectionendtoend_unittest.cc b/app/webrtc/peerconnectionendtoend_unittest.cc
new file mode 100644
index 0000000..da3c03d
--- /dev/null
+++ b/app/webrtc/peerconnectionendtoend_unittest.cc
@@ -0,0 +1,224 @@
+/*
+ * 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/test/peerconnectiontestwrapper.h"
+#include "talk/base/gunit.h"
+#include "talk/base/logging.h"
+#include "talk/base/ssladapter.h"
+#include "talk/base/sslstreamadapter.h"
+#include "talk/base/stringencode.h"
+#include "talk/base/stringutils.h"
+
+using webrtc::FakeConstraints;
+using webrtc::MediaConstraintsInterface;
+using webrtc::MediaStreamInterface;
+using webrtc::PeerConnectionInterface;
+
+namespace {
+
+const char kExternalGiceUfrag[] = "1234567890123456";
+const char kExternalGicePwd[] = "123456789012345678901234";
+
+void RemoveLinesFromSdp(const std::string& line_start,
+                               std::string* sdp) {
+  const char kSdpLineEnd[] = "\r\n";
+  size_t ssrc_pos = 0;
+  while ((ssrc_pos = sdp->find(line_start, ssrc_pos)) !=
+      std::string::npos) {
+    size_t end_ssrc = sdp->find(kSdpLineEnd, ssrc_pos);
+    sdp->erase(ssrc_pos, end_ssrc - ssrc_pos + strlen(kSdpLineEnd));
+  }
+}
+
+// Add |newlines| to the |message| after |line|.
+void InjectAfter(const std::string& line,
+                 const std::string& newlines,
+                 std::string* message) {
+  const std::string tmp = line + newlines;
+  talk_base::replace_substrs(line.c_str(), line.length(),
+                             tmp.c_str(), tmp.length(), message);
+}
+
+void Replace(const std::string& line,
+             const std::string& newlines,
+             std::string* message) {
+  talk_base::replace_substrs(line.c_str(), line.length(),
+                             newlines.c_str(), newlines.length(), message);
+}
+
+void UseExternalSdes(std::string* sdp) {
+  // Remove current crypto specification.
+  RemoveLinesFromSdp("a=crypto", sdp);
+  RemoveLinesFromSdp("a=fingerprint", sdp);
+  // Add external crypto.
+  const char kAudioSdes[] =
+      "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
+      "inline:PS1uQCVeeCFCanVmcjkpPywjNWhcYD0mXXtxaVBR\r\n";
+  const char kVideoSdes[] =
+      "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
+      "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj\r\n";
+  const char kDataSdes[] =
+      "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
+      "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj\r\n";
+  InjectAfter("a=mid:audio\r\n", kAudioSdes, sdp);
+  InjectAfter("a=mid:video\r\n", kVideoSdes, sdp);
+  InjectAfter("a=mid:data\r\n", kDataSdes, sdp);
+}
+
+void UseGice(std::string* sdp) {
+  InjectAfter("t=0 0\r\n", "a=ice-options:google-ice\r\n", sdp);
+
+  std::string ufragline = "a=ice-ufrag:";
+  std::string pwdline = "a=ice-pwd:";
+  RemoveLinesFromSdp(ufragline, sdp);
+  RemoveLinesFromSdp(pwdline, sdp);
+  ufragline.append(kExternalGiceUfrag);
+  ufragline.append("\r\n");
+  pwdline.append(kExternalGicePwd);
+  pwdline.append("\r\n");
+  const std::string ufrag_pwd = ufragline + pwdline;
+
+  InjectAfter("a=mid:audio\r\n", ufrag_pwd, sdp);
+  InjectAfter("a=mid:video\r\n", ufrag_pwd, sdp);
+  InjectAfter("a=mid:data\r\n", ufrag_pwd, sdp);
+}
+
+void RemoveBundle(std::string* sdp) {
+  RemoveLinesFromSdp("a=group:BUNDLE", sdp);
+}
+
+}  // namespace
+
+class PeerConnectionEndToEndTest
+    : public sigslot::has_slots<>,
+      public testing::Test {
+ public:
+  PeerConnectionEndToEndTest()
+      : caller_(new talk_base::RefCountedObject<PeerConnectionTestWrapper>(
+                    "caller")),
+        callee_(new talk_base::RefCountedObject<PeerConnectionTestWrapper>(
+                    "callee")) {
+    talk_base::InitializeSSL(NULL);
+  }
+
+  void CreatePcs() {
+    CreatePcs(NULL);
+  }
+
+  void CreatePcs(const MediaConstraintsInterface* pc_constraints) {
+    EXPECT_TRUE(caller_->CreatePc(pc_constraints));
+    EXPECT_TRUE(callee_->CreatePc(pc_constraints));
+    PeerConnectionTestWrapper::Connect(caller_.get(), callee_.get());
+  }
+
+  void GetAndAddUserMedia() {
+    FakeConstraints audio_constraints;
+    FakeConstraints video_constraints;
+    GetAndAddUserMedia(true, audio_constraints, true, video_constraints);
+  }
+
+  void GetAndAddUserMedia(bool audio, FakeConstraints audio_constraints,
+                          bool video, FakeConstraints video_constraints) {
+    caller_->GetAndAddUserMedia(audio, audio_constraints,
+                                video, video_constraints);
+    callee_->GetAndAddUserMedia(audio, audio_constraints,
+                                video, video_constraints);
+  }
+
+  void Negotiate() {
+    caller_->CreateOffer(NULL);
+  }
+
+  void WaitForCallEstablished() {
+    caller_->WaitForCallEstablished();
+    callee_->WaitForCallEstablished();
+  }
+
+  void SetupLegacySdpConverter() {
+    caller_->SignalOnSdpCreated.connect(
+      this, &PeerConnectionEndToEndTest::ConvertToLegacySdp);
+    callee_->SignalOnSdpCreated.connect(
+      this, &PeerConnectionEndToEndTest::ConvertToLegacySdp);
+  }
+
+  void ConvertToLegacySdp(std::string* sdp) {
+    UseExternalSdes(sdp);
+    UseGice(sdp);
+    RemoveBundle(sdp);
+    LOG(LS_INFO) << "ConvertToLegacySdp: " << *sdp;
+  }
+
+  void SetupGiceConverter() {
+    caller_->SignalOnIceCandidateCreated.connect(
+      this, &PeerConnectionEndToEndTest::AddGiceCredsToCandidate);
+    callee_->SignalOnIceCandidateCreated.connect(
+      this, &PeerConnectionEndToEndTest::AddGiceCredsToCandidate);
+  }
+
+  void AddGiceCredsToCandidate(std::string* sdp) {
+    std::string gice_creds = " username ";
+    gice_creds.append(kExternalGiceUfrag);
+    gice_creds.append(" password ");
+    gice_creds.append(kExternalGicePwd);
+    gice_creds.append("\r\n");
+    Replace("\r\n", gice_creds, sdp);
+    LOG(LS_INFO) << "AddGiceCredsToCandidate: " << *sdp;
+  }
+
+  ~PeerConnectionEndToEndTest() {
+    talk_base::CleanupSSL();
+  }
+
+ protected:
+  talk_base::scoped_refptr<PeerConnectionTestWrapper> caller_;
+  talk_base::scoped_refptr<PeerConnectionTestWrapper> callee_;
+};
+
+// Disable for TSan v2, see
+// https://code.google.com/p/webrtc/issues/detail?id=1205 for details.
+#if !defined(THREAD_SANITIZER)
+
+TEST_F(PeerConnectionEndToEndTest, Call) {
+  CreatePcs();
+  GetAndAddUserMedia();
+  Negotiate();
+  WaitForCallEstablished();
+}
+
+TEST_F(PeerConnectionEndToEndTest, CallWithLegacySdp) {
+  FakeConstraints pc_constraints;
+  pc_constraints.AddMandatory(MediaConstraintsInterface::kEnableDtlsSrtp,
+                              false);
+  CreatePcs(&pc_constraints);
+  SetupLegacySdpConverter();
+  SetupGiceConverter();
+  GetAndAddUserMedia();
+  Negotiate();
+  WaitForCallEstablished();
+}
+
+#endif // if !defined(THREAD_SANITIZER)
diff --git a/app/webrtc/statscollector.cc b/app/webrtc/statscollector.cc
index db7cac4..57277b6 100644
--- a/app/webrtc/statscollector.cc
+++ b/app/webrtc/statscollector.cc
@@ -100,6 +100,8 @@
 const char StatsReport::kStatsValueNameIssuerId[] = "googIssuerId";
 const char StatsReport::kStatsValueNameJitterReceived[] = "googJitterReceived";
 const char StatsReport::kStatsValueNameLocalAddress[] = "googLocalAddress";
+const char StatsReport::kStatsValueNameLocalCandidateType[] =
+    "googLocalCandidateType";
 const char StatsReport::kStatsValueNameLocalCertificateId[] =
     "googLocalCertificateId";
 const char StatsReport::kStatsValueNameNacksReceived[] = "googNacksReceived";
@@ -109,6 +111,8 @@
 const char StatsReport::kStatsValueNamePacketsLost[] = "packetsLost";
 const char StatsReport::kStatsValueNameReadable[] = "googReadable";
 const char StatsReport::kStatsValueNameRemoteAddress[] = "googRemoteAddress";
+const char StatsReport::kStatsValueNameRemoteCandidateType[] =
+    "googRemoteCandidateType";
 const char StatsReport::kStatsValueNameRemoteCertificateId[] =
     "googRemoteCertificateId";
 const char StatsReport::kStatsValueNameRetransmitBitrate[] =
@@ -678,6 +682,10 @@
           report.AddValue(StatsReport::kStatsValueNameRtt, info.rtt);
           report.AddValue(StatsReport::kStatsValueNameTransportType,
                           info.local_candidate.protocol());
+          report.AddValue(StatsReport::kStatsValueNameLocalCandidateType,
+                          info.local_candidate.type());
+          report.AddValue(StatsReport::kStatsValueNameRemoteCandidateType,
+                          info.remote_candidate.type());
           reports_[report.id] = report;
         }
       }
diff --git a/app/webrtc/statstypes.h b/app/webrtc/statstypes.h
index e76aa86..11a8146 100644
--- a/app/webrtc/statstypes.h
+++ b/app/webrtc/statstypes.h
@@ -177,6 +177,8 @@
   static const char kStatsValueNameIssuerId[];
   static const char kStatsValueNameLocalCertificateId[];
   static const char kStatsValueNameRemoteCertificateId[];
+  static const char kStatsValueNameLocalCandidateType[];
+  static const char kStatsValueNameRemoteCandidateType[];
 };
 
 typedef std::vector<StatsReport> StatsReports;
diff --git a/app/webrtc/test/peerconnectiontestwrapper.cc b/app/webrtc/test/peerconnectiontestwrapper.cc
new file mode 100644
index 0000000..c22ecaf
--- /dev/null
+++ b/app/webrtc/test/peerconnectiontestwrapper.cc
@@ -0,0 +1,278 @@
+/*
+ * 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/fakeportallocatorfactory.h"
+#include "talk/app/webrtc/test/fakeperiodicvideocapturer.h"
+#include "talk/app/webrtc/test/mockpeerconnectionobservers.h"
+#include "talk/app/webrtc/test/peerconnectiontestwrapper.h"
+#include "talk/app/webrtc/videosourceinterface.h"
+#include "talk/base/gunit.h"
+
+static const char kStreamLabelBase[] = "stream_label";
+static const char kVideoTrackLabelBase[] = "video_track";
+static const char kAudioTrackLabelBase[] = "audio_track";
+static const int kMaxWait = 5000;
+static const int kTestAudioFrameCount = 3;
+static const int kTestVideoFrameCount = 3;
+
+using webrtc::FakeConstraints;
+using webrtc::FakeVideoTrackRenderer;
+using webrtc::IceCandidateInterface;
+using webrtc::MediaConstraintsInterface;
+using webrtc::MediaStreamInterface;
+using webrtc::MockSetSessionDescriptionObserver;
+using webrtc::PeerConnectionInterface;
+using webrtc::SessionDescriptionInterface;
+using webrtc::VideoTrackInterface;
+
+void PeerConnectionTestWrapper::Connect(PeerConnectionTestWrapper* caller,
+                                        PeerConnectionTestWrapper* callee) {
+  caller->SignalOnIceCandidateReady.connect(
+      callee, &PeerConnectionTestWrapper::AddIceCandidate);
+  callee->SignalOnIceCandidateReady.connect(
+      caller, &PeerConnectionTestWrapper::AddIceCandidate);
+
+  caller->SignalOnSdpReady.connect(
+      callee, &PeerConnectionTestWrapper::ReceiveOfferSdp);
+  callee->SignalOnSdpReady.connect(
+      caller, &PeerConnectionTestWrapper::ReceiveAnswerSdp);
+}
+
+PeerConnectionTestWrapper::PeerConnectionTestWrapper(const std::string& name)
+    : name_(name) {}
+
+PeerConnectionTestWrapper::~PeerConnectionTestWrapper() {}
+
+bool PeerConnectionTestWrapper::CreatePc(
+  const MediaConstraintsInterface* constraints) {
+  allocator_factory_ = webrtc::FakePortAllocatorFactory::Create();
+  if (!allocator_factory_) {
+    return false;
+  }
+
+  audio_thread_.Start();
+  fake_audio_capture_module_ = FakeAudioCaptureModule::Create(
+      &audio_thread_);
+  if (fake_audio_capture_module_ == NULL) {
+    return false;
+  }
+
+  peer_connection_factory_ = webrtc::CreatePeerConnectionFactory(
+      talk_base::Thread::Current(), talk_base::Thread::Current(),
+      fake_audio_capture_module_, NULL, NULL);
+  if (!peer_connection_factory_) {
+    return false;
+  }
+
+  // CreatePeerConnection with IceServers.
+  webrtc::PeerConnectionInterface::IceServers ice_servers;
+  webrtc::PeerConnectionInterface::IceServer ice_server;
+  ice_server.uri = "stun:stun.l.google.com:19302";
+  ice_servers.push_back(ice_server);
+  peer_connection_ = peer_connection_factory_->CreatePeerConnection(
+      ice_servers, constraints, allocator_factory_.get(), NULL, this);
+
+  return peer_connection_.get() != NULL;
+}
+
+void PeerConnectionTestWrapper::OnAddStream(MediaStreamInterface* stream) {
+  LOG(LS_INFO) << "PeerConnectionTestWrapper " << name_
+               << ": OnAddStream";
+  // TODO(ronghuawu): support multiple streams.
+  if (stream->GetVideoTracks().size() > 0) {
+    renderer_.reset(new FakeVideoTrackRenderer(stream->GetVideoTracks()[0]));
+  }
+}
+
+void PeerConnectionTestWrapper::OnIceCandidate(
+    const IceCandidateInterface* candidate) {
+  std::string sdp;
+  EXPECT_TRUE(candidate->ToString(&sdp));
+  // Give the user a chance to modify sdp for testing.
+  SignalOnIceCandidateCreated(&sdp);
+  SignalOnIceCandidateReady(candidate->sdp_mid(), candidate->sdp_mline_index(),
+                            sdp);
+}
+
+void PeerConnectionTestWrapper::OnSuccess(SessionDescriptionInterface* desc) {
+  std::string sdp;
+  EXPECT_TRUE(desc->ToString(&sdp));
+
+  LOG(LS_INFO) << "PeerConnectionTestWrapper " << name_
+               << ": " << desc->type() << " sdp created: " << sdp;
+
+  // Give the user a chance to modify sdp for testing.
+  SignalOnSdpCreated(&sdp);
+
+  SetLocalDescription(desc->type(), sdp);
+
+  SignalOnSdpReady(sdp);
+}
+
+void PeerConnectionTestWrapper::CreateOffer(
+    const MediaConstraintsInterface* constraints) {
+  LOG(LS_INFO) << "PeerConnectionTestWrapper " << name_
+               << ": CreateOffer.";
+  peer_connection_->CreateOffer(this, constraints);
+}
+
+void PeerConnectionTestWrapper::CreateAnswer(
+    const MediaConstraintsInterface* constraints) {
+  LOG(LS_INFO) << "PeerConnectionTestWrapper " << name_
+               << ": CreateAnswer.";
+  peer_connection_->CreateAnswer(this, constraints);
+}
+
+void PeerConnectionTestWrapper::ReceiveOfferSdp(const std::string& sdp) {
+  SetRemoteDescription(SessionDescriptionInterface::kOffer, sdp);
+  CreateAnswer(NULL);
+}
+
+void PeerConnectionTestWrapper::ReceiveAnswerSdp(const std::string& sdp) {
+  SetRemoteDescription(SessionDescriptionInterface::kAnswer, sdp);
+}
+
+void PeerConnectionTestWrapper::SetLocalDescription(const std::string& type,
+                                                    const std::string& sdp) {
+  LOG(LS_INFO) << "PeerConnectionTestWrapper " << name_
+               << ": SetLocalDescription " << type << " " << sdp;
+
+  talk_base::scoped_refptr<MockSetSessionDescriptionObserver>
+      observer(new talk_base::RefCountedObject<
+                   MockSetSessionDescriptionObserver>());
+  peer_connection_->SetLocalDescription(
+      observer, webrtc::CreateSessionDescription(type, sdp, NULL));
+}
+
+void PeerConnectionTestWrapper::SetRemoteDescription(const std::string& type,
+                                                     const std::string& sdp) {
+  LOG(LS_INFO) << "PeerConnectionTestWrapper " << name_
+               << ": SetRemoteDescription " << type << " " << sdp;
+
+  talk_base::scoped_refptr<MockSetSessionDescriptionObserver>
+      observer(new talk_base::RefCountedObject<
+                   MockSetSessionDescriptionObserver>());
+  peer_connection_->SetRemoteDescription(
+      observer, webrtc::CreateSessionDescription(type, sdp, NULL));
+}
+
+void PeerConnectionTestWrapper::AddIceCandidate(const std::string& sdp_mid,
+                                                int sdp_mline_index,
+                                                const std::string& candidate) {
+  EXPECT_TRUE(peer_connection_->AddIceCandidate(
+                  webrtc::CreateIceCandidate(sdp_mid, sdp_mline_index,
+                                             candidate, NULL)));
+}
+
+void PeerConnectionTestWrapper::WaitForCallEstablished() {
+  WaitForConnection();
+  WaitForAudio();
+  WaitForVideo();
+}
+
+void PeerConnectionTestWrapper::WaitForConnection() {
+  EXPECT_TRUE_WAIT(CheckForConnection(), kMaxWait);
+  LOG(LS_INFO) << "PeerConnectionTestWrapper " << name_
+               << ": Connected.";
+}
+
+bool PeerConnectionTestWrapper::CheckForConnection() {
+  return (peer_connection_->ice_connection_state() ==
+          PeerConnectionInterface::kIceConnectionConnected);
+}
+
+void PeerConnectionTestWrapper::WaitForAudio() {
+  EXPECT_TRUE_WAIT(CheckForAudio(), kMaxWait);
+  LOG(LS_INFO) << "PeerConnectionTestWrapper " << name_
+               << ": Got enough audio frames.";
+}
+
+bool PeerConnectionTestWrapper::CheckForAudio() {
+  return (fake_audio_capture_module_->frames_received() >=
+          kTestAudioFrameCount);
+}
+
+void PeerConnectionTestWrapper::WaitForVideo() {
+  EXPECT_TRUE_WAIT(CheckForVideo(), kMaxWait);
+  LOG(LS_INFO) << "PeerConnectionTestWrapper " << name_
+               << ": Got enough video frames.";
+}
+
+bool PeerConnectionTestWrapper::CheckForVideo() {
+  if (!renderer_) {
+    return false;
+  }
+  return (renderer_->num_rendered_frames() >= kTestVideoFrameCount);
+}
+
+void PeerConnectionTestWrapper::GetAndAddUserMedia(
+    bool audio, const webrtc::FakeConstraints& audio_constraints,
+    bool video, const webrtc::FakeConstraints& video_constraints) {
+  talk_base::scoped_refptr<webrtc::MediaStreamInterface> stream =
+      GetUserMedia(audio, audio_constraints, video, video_constraints);
+  EXPECT_TRUE(peer_connection_->AddStream(stream, NULL));
+}
+
+talk_base::scoped_refptr<webrtc::MediaStreamInterface>
+    PeerConnectionTestWrapper::GetUserMedia(
+        bool audio, const webrtc::FakeConstraints& audio_constraints,
+        bool video, const webrtc::FakeConstraints& video_constraints) {
+  std::string label = kStreamLabelBase +
+      talk_base::ToString<int>(
+          static_cast<int>(peer_connection_->local_streams()->count()));
+  talk_base::scoped_refptr<webrtc::MediaStreamInterface> stream =
+      peer_connection_factory_->CreateLocalMediaStream(label);
+
+  if (audio) {
+    FakeConstraints constraints = audio_constraints;
+    // Disable highpass filter so that we can get all the test audio frames.
+    constraints.AddMandatory(
+        MediaConstraintsInterface::kHighpassFilter, false);
+    talk_base::scoped_refptr<webrtc::AudioSourceInterface> source =
+        peer_connection_factory_->CreateAudioSource(&constraints);
+    talk_base::scoped_refptr<webrtc::AudioTrackInterface> audio_track(
+        peer_connection_factory_->CreateAudioTrack(kAudioTrackLabelBase,
+                                                   source));
+    stream->AddTrack(audio_track);
+  }
+
+  if (video) {
+    // Set max frame rate to 10fps to reduce the risk of the tests to be flaky.
+    FakeConstraints constraints = video_constraints;
+    constraints.SetMandatoryMaxFrameRate(10);
+
+    talk_base::scoped_refptr<webrtc::VideoSourceInterface> source =
+        peer_connection_factory_->CreateVideoSource(
+            new webrtc::FakePeriodicVideoCapturer(), &constraints);
+    std::string videotrack_label = label + kVideoTrackLabelBase;
+    talk_base::scoped_refptr<webrtc::VideoTrackInterface> video_track(
+        peer_connection_factory_->CreateVideoTrack(videotrack_label, source));
+
+    stream->AddTrack(video_track);
+  }
+  return stream;
+}
diff --git a/app/webrtc/test/peerconnectiontestwrapper.h b/app/webrtc/test/peerconnectiontestwrapper.h
new file mode 100644
index 0000000..46fefaf
--- /dev/null
+++ b/app/webrtc/test/peerconnectiontestwrapper.h
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+#ifndef TALK_APP_WEBRTC_TEST_PEERCONNECTIONTESTWRAPPER_H_
+#define TALK_APP_WEBRTC_TEST_PEERCONNECTIONTESTWRAPPER_H_
+
+#include "talk/app/webrtc/peerconnectioninterface.h"
+#include "talk/app/webrtc/test/fakeaudiocapturemodule.h"
+#include "talk/app/webrtc/test/fakeconstraints.h"
+#include "talk/app/webrtc/test/fakevideotrackrenderer.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/thread.h"
+
+namespace webrtc {
+class PortAllocatorFactoryInterface;
+}
+
+class PeerConnectionTestWrapper
+    : public webrtc::PeerConnectionObserver,
+      public webrtc::CreateSessionDescriptionObserver,
+      public sigslot::has_slots<> {
+ public:
+  static void Connect(PeerConnectionTestWrapper* caller,
+                      PeerConnectionTestWrapper* callee);
+
+  explicit PeerConnectionTestWrapper(const std::string& name);
+  virtual ~PeerConnectionTestWrapper();
+
+  bool CreatePc(const webrtc::MediaConstraintsInterface* constraints);
+
+  // Implements PeerConnectionObserver.
+  virtual void OnError() {}
+  virtual void OnSignalingChange(
+     webrtc::PeerConnectionInterface::SignalingState new_state) {}
+  virtual void OnStateChange(
+      webrtc::PeerConnectionObserver::StateType state_changed) {}
+  virtual void OnAddStream(webrtc::MediaStreamInterface* stream);
+  virtual void OnRemoveStream(webrtc::MediaStreamInterface* stream) {}
+  virtual void OnDataChannel(webrtc::DataChannelInterface* data_channel) {}
+  virtual void OnRenegotiationNeeded() {}
+  virtual void OnIceConnectionChange(
+      webrtc::PeerConnectionInterface::IceConnectionState new_state) {}
+  virtual void OnIceGatheringChange(
+      webrtc::PeerConnectionInterface::IceGatheringState new_state) {}
+  virtual void OnIceCandidate(const webrtc::IceCandidateInterface* candidate);
+  virtual void OnIceComplete() {}
+
+  // Implements CreateSessionDescriptionObserver.
+  virtual void OnSuccess(webrtc::SessionDescriptionInterface* desc);
+  virtual void OnFailure(const std::string& error) {}
+
+  void CreateOffer(const webrtc::MediaConstraintsInterface* constraints);
+  void CreateAnswer(const webrtc::MediaConstraintsInterface* constraints);
+  void ReceiveOfferSdp(const std::string& sdp);
+  void ReceiveAnswerSdp(const std::string& sdp);
+  void AddIceCandidate(const std::string& sdp_mid, int sdp_mline_index,
+                       const std::string& candidate);
+  void WaitForCallEstablished();
+  void WaitForConnection();
+  void WaitForAudio();
+  void WaitForVideo();
+  void GetAndAddUserMedia(
+    bool audio, const webrtc::FakeConstraints& audio_constraints,
+    bool video, const webrtc::FakeConstraints& video_constraints);
+
+  // sigslots
+  sigslot::signal1<std::string*> SignalOnIceCandidateCreated;
+  sigslot::signal3<const std::string&,
+                   int,
+                   const std::string&> SignalOnIceCandidateReady;
+  sigslot::signal1<std::string*> SignalOnSdpCreated;
+  sigslot::signal1<const std::string&> SignalOnSdpReady;
+
+ private:
+  void SetLocalDescription(const std::string& type, const std::string& sdp);
+  void SetRemoteDescription(const std::string& type, const std::string& sdp);
+  bool CheckForConnection();
+  bool CheckForAudio();
+  bool CheckForVideo();
+  talk_base::scoped_refptr<webrtc::MediaStreamInterface> GetUserMedia(
+      bool audio, const webrtc::FakeConstraints& audio_constraints,
+      bool video, const webrtc::FakeConstraints& video_constraints);
+
+  std::string name_;
+  talk_base::Thread audio_thread_;
+  talk_base::scoped_refptr<webrtc::PortAllocatorFactoryInterface>
+      allocator_factory_;
+  talk_base::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection_;
+  talk_base::scoped_refptr<webrtc::PeerConnectionFactoryInterface>
+      peer_connection_factory_;
+  talk_base::scoped_refptr<FakeAudioCaptureModule> fake_audio_capture_module_;
+  talk_base::scoped_ptr<webrtc::FakeVideoTrackRenderer> renderer_;
+};
+
+#endif  // TALK_APP_WEBRTC_TEST_PEERCONNECTIONTESTWRAPPER_H_
diff --git a/app/webrtc/webrtcsession.cc b/app/webrtc/webrtcsession.cc
index 565eee3..7e153b3 100644
--- a/app/webrtc/webrtcsession.cc
+++ b/app/webrtc/webrtcsession.cc
@@ -526,7 +526,7 @@
       this, &WebRtcSession::OnIdentityReady);
 
   if (options.disable_encryption) {
-    webrtc_session_desc_factory_->set_secure(cricket::SEC_DISABLED);
+    webrtc_session_desc_factory_->SetSecure(cricket::SEC_DISABLED);
   }
 
   return true;
@@ -554,13 +554,13 @@
   return true;
 }
 
-void WebRtcSession::set_secure_policy(
+void WebRtcSession::SetSecurePolicy(
     cricket::SecureMediaPolicy secure_policy) {
-  webrtc_session_desc_factory_->set_secure(secure_policy);
+  webrtc_session_desc_factory_->SetSecure(secure_policy);
 }
 
-cricket::SecureMediaPolicy WebRtcSession::secure_policy() const {
-  return webrtc_session_desc_factory_->secure();
+cricket::SecureMediaPolicy WebRtcSession::SecurePolicy() const {
+  return webrtc_session_desc_factory_->Secure();
 }
 
 bool WebRtcSession::GetSslRole(talk_base::SSLRole* role) {
@@ -610,7 +610,7 @@
   }
 
   cricket::SecureMediaPolicy secure_policy =
-      webrtc_session_desc_factory_->secure();
+      webrtc_session_desc_factory_->Secure();
   // Update the MediaContentDescription crypto settings as per the policy set.
   UpdateSessionDescriptionSecurePolicy(secure_policy, desc->description());
 
@@ -1483,7 +1483,7 @@
 
   // Verify crypto settings.
   std::string crypto_error;
-  if (webrtc_session_desc_factory_->secure() == cricket::SEC_REQUIRED &&
+  if (webrtc_session_desc_factory_->Secure() == cricket::SEC_REQUIRED &&
       !VerifyCrypto(sdesc->description(), dtls_enabled_, &crypto_error)) {
     return BadSdp(source, crypto_error, error_desc);
   }
diff --git a/app/webrtc/webrtcsession.h b/app/webrtc/webrtcsession.h
index da994c5..4c83906 100644
--- a/app/webrtc/webrtcsession.h
+++ b/app/webrtc/webrtcsession.h
@@ -42,20 +42,17 @@
 #include "talk/session/media/mediasession.h"
 
 namespace cricket {
-
+class BaseChannel;
 class ChannelManager;
 class DataChannel;
 class StatsReport;
 class Transport;
 class VideoCapturer;
-class BaseChannel;
 class VideoChannel;
 class VoiceChannel;
-
 }  // namespace cricket
 
 namespace webrtc {
-
 class IceRestartAnswerLatch;
 class MediaStreamSignaling;
 class WebRtcSessionDescriptionFactory;
@@ -79,6 +76,7 @@
 // ICE state callback interface.
 class IceObserver {
  public:
+  IceObserver() {}
   // Called any time the IceConnectionState changes
   virtual void OnIceConnectionChange(
       PeerConnectionInterface::IceConnectionState new_state) {}
@@ -94,6 +92,9 @@
 
  protected:
   ~IceObserver() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(IceObserver);
 };
 
 class WebRtcSession : public cricket::BaseSession,
@@ -131,8 +132,8 @@
     return data_channel_.get();
   }
 
-  void set_secure_policy(cricket::SecureMediaPolicy secure_policy);
-  cricket::SecureMediaPolicy secure_policy() const;
+  void SetSecurePolicy(cricket::SecureMediaPolicy secure_policy);
+  cricket::SecureMediaPolicy SecurePolicy() const;
 
   // Get current ssl role from transport.
   bool GetSslRole(talk_base::SSLRole* role);
@@ -330,8 +331,9 @@
   sigslot::signal0<> SignalVoiceChannelDestroyed;
   sigslot::signal0<> SignalVideoChannelDestroyed;
   sigslot::signal0<> SignalDataChannelDestroyed;
-};
 
+  DISALLOW_COPY_AND_ASSIGN(WebRtcSession);
+};
 }  // namespace webrtc
 
 #endif  // TALK_APP_WEBRTC_WEBRTCSESSION_H_
diff --git a/app/webrtc/webrtcsession_unittest.cc b/app/webrtc/webrtcsession_unittest.cc
index 0ddc16c..5ec880a 100644
--- a/app/webrtc/webrtcsession_unittest.cc
+++ b/app/webrtc/webrtcsession_unittest.cc
@@ -86,20 +86,21 @@
 using webrtc::SessionDescriptionInterface;
 using webrtc::StreamCollection;
 using webrtc::WebRtcSession;
+using webrtc::kBundleWithoutRtcpMux;
 using webrtc::kMlineMismatch;
+using webrtc::kPushDownAnswerTDFailed;
+using webrtc::kPushDownPranswerTDFailed;
 using webrtc::kSdpWithoutCrypto;
-using webrtc::kSdpWithoutSdesAndDtlsDisabled;
 using webrtc::kSdpWithoutIceUfragPwd;
+using webrtc::kSdpWithoutSdesAndDtlsDisabled;
 using webrtc::kSessionError;
 using webrtc::kSetLocalSdpFailed;
 using webrtc::kSetRemoteSdpFailed;
-using webrtc::kPushDownAnswerTDFailed;
-using webrtc::kPushDownPranswerTDFailed;
-using webrtc::kBundleWithoutRtcpMux;
 
-static const SocketAddress kClientAddr1("11.11.11.11", 0);
-static const SocketAddress kClientAddr2("22.22.22.22", 0);
-static const SocketAddress kStunAddr("99.99.99.1", cricket::STUN_SERVER_PORT);
+static const int kClientAddrPort = 0;
+static const char kClientAddrHost1[] = "11.11.11.11";
+static const char kClientAddrHost2[] = "22.22.22.22";
+static const char kStunAddrHost[] = "99.99.99.1";
 
 static const char kSessionVersion[] = "1";
 
@@ -113,11 +114,6 @@
 
 static const int kIceCandidatesTimeout = 10000;
 
-static const cricket::AudioCodec
-    kTelephoneEventCodec(106, "telephone-event", 8000, 0, 1, 0);
-static const cricket::AudioCodec kCNCodec1(102, "CN", 8000, 0, 1, 0);
-static const cricket::AudioCodec kCNCodec2(103, "CN", 16000, 0, 1, 0);
-
 static const char kFakeDtlsFingerprint[] =
     "BB:CD:72:F7:2F:D0:BA:43:F3:68:B1:0C:23:72:B6:4A:"
     "0F:DE:34:06:BC:E0:FE:01:BC:73:C8:6D:F4:65:D5:24";
@@ -159,11 +155,17 @@
 
   // Found a new candidate.
   virtual void OnIceCandidate(const webrtc::IceCandidateInterface* candidate) {
-    if (candidate->sdp_mline_index() == kMediaContentIndex0) {
-      mline_0_candidates_.push_back(candidate->candidate());
-    } else if (candidate->sdp_mline_index() == kMediaContentIndex1) {
-      mline_1_candidates_.push_back(candidate->candidate());
+    switch (candidate->sdp_mline_index()) {
+      case kMediaContentIndex0:
+        mline_0_candidates_.push_back(candidate->candidate());
+        break;
+      case kMediaContentIndex1:
+        mline_1_candidates_.push_back(candidate->candidate());
+        break;
+      default:
+        ASSERT(false);
     }
+
     // The ICE gathering state should always be Gathering when a candidate is
     // received (or possibly Completed in the case of the final candidate).
     EXPECT_NE(PeerConnectionInterface::kIceGatheringNew, ice_gathering_state_);
@@ -281,8 +283,10 @@
       vss_(new talk_base::VirtualSocketServer(pss_.get())),
       fss_(new talk_base::FirewallSocketServer(vss_.get())),
       ss_scope_(fss_.get()),
-      stun_server_(talk_base::Thread::Current(), kStunAddr),
-      allocator_(&network_manager_, kStunAddr,
+      stun_socket_addr_(talk_base::SocketAddress(kStunAddrHost,
+                                                 cricket::STUN_SERVER_PORT)),
+      stun_server_(talk_base::Thread::Current(), stun_socket_addr_),
+      allocator_(&network_manager_, stun_socket_addr_,
                  SocketAddress(), SocketAddress(), SocketAddress()),
       mediastream_signaling_(channel_manager_.get()) {
     tdesc_factory_->set_protocol(cricket::ICEPROTO_HYBRID);
@@ -324,6 +328,8 @@
 
   void InitWithDtmfCodec() {
     // Add kTelephoneEventCodec for dtmf test.
+    const cricket::AudioCodec kTelephoneEventCodec(
+        106, "telephone-event", 8000, 0, 1, 0);
     std::vector<cricket::AudioCodec> codecs;
     codecs.push_back(kTelephoneEventCodec);
     media_engine_->SetAudioCodecs(codecs);
@@ -370,12 +376,12 @@
     return observer->ReleaseDescription();
   }
 
-  bool ChannelsExist() {
+  bool ChannelsExist() const {
     return (session_->voice_channel() != NULL &&
             session_->video_channel() != NULL);
   }
 
-  void CheckTransportChannels() {
+  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);
@@ -710,7 +716,7 @@
   }
 
   void TestSessionCandidatesWithBundleRtcpMux(bool bundle, bool rtcp_mux) {
-    AddInterface(kClientAddr1);
+    AddInterface(talk_base::SocketAddress(kClientAddrHost1, kClientAddrPort));
     Init(NULL);
     mediastream_signaling_.SendAudioVideoStream1();
     FakeConstraints constraints;
@@ -780,7 +786,7 @@
   // New -> Checking -> Connected -> Disconnected -> Connected.
   // The Gathering state should go: New -> Gathering -> Completed.
   void TestLoopbackCall() {
-    AddInterface(kClientAddr1);
+    AddInterface(talk_base::SocketAddress(kClientAddrHost1, kClientAddrPort));
     Init(NULL);
     mediastream_signaling_.SendAudioVideoStream1();
     SessionDescriptionInterface* offer = CreateOffer(NULL);
@@ -815,7 +821,10 @@
 
     // Adding firewall rule to block ping requests, which should cause
     // transport channel failure.
-    fss_->AddRule(false, talk_base::FP_ANY, talk_base::FD_ANY, kClientAddr1);
+    fss_->AddRule(false,
+                  talk_base::FP_ANY,
+                  talk_base::FD_ANY,
+                  talk_base::SocketAddress(kClientAddrHost1, kClientAddrPort));
     EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionDisconnected,
                    observer_.ice_connection_state_,
                    kIceCandidatesTimeout);
@@ -840,7 +849,10 @@
 
   // Adds CN codecs to FakeMediaEngine and MediaDescriptionFactory.
   void AddCNCodecs() {
-    // Add kTelephoneEventCodec for dtmf test.
+    const cricket::AudioCodec kCNCodec1(102, "CN", 8000, 0, 1, 0);
+    const cricket::AudioCodec kCNCodec2(103, "CN", 16000, 0, 1, 0);
+
+    // Add kCNCodec for dtmf test.
     std::vector<cricket::AudioCodec> codecs = media_engine_->audio_codecs();;
     codecs.push_back(kCNCodec1);
     codecs.push_back(kCNCodec2);
@@ -918,6 +930,7 @@
   talk_base::scoped_ptr<talk_base::VirtualSocketServer> vss_;
   talk_base::scoped_ptr<talk_base::FirewallSocketServer> fss_;
   talk_base::SocketServerScope ss_scope_;
+  talk_base::SocketAddress stun_socket_addr_;
   cricket::TestStunServer stun_server_;
   talk_base::FakeNetworkManager network_manager_;
   cricket::BasicPortAllocator allocator_;
@@ -941,7 +954,7 @@
 // Verifies that WebRtcSession uses SEC_REQUIRED by default.
 TEST_F(WebRtcSessionTest, TestDefaultSetSecurePolicy) {
   Init(NULL);
-  EXPECT_EQ(cricket::SEC_REQUIRED, session_->secure_policy());
+  EXPECT_EQ(cricket::SEC_REQUIRED, session_->SecurePolicy());
 }
 
 TEST_F(WebRtcSessionTest, TestSessionCandidates) {
@@ -959,8 +972,8 @@
 }
 
 TEST_F(WebRtcSessionTest, TestMultihomeCandidates) {
-  AddInterface(kClientAddr1);
-  AddInterface(kClientAddr2);
+  AddInterface(talk_base::SocketAddress(kClientAddrHost1, kClientAddrPort));
+  AddInterface(talk_base::SocketAddress(kClientAddrHost2, kClientAddrPort));
   Init(NULL);
   mediastream_signaling_.SendAudioVideoStream1();
   InitiateCall();
@@ -970,13 +983,16 @@
 }
 
 TEST_F(WebRtcSessionTest, TestStunError) {
-  AddInterface(kClientAddr1);
-  AddInterface(kClientAddr2);
-  fss_->AddRule(false, talk_base::FP_UDP, talk_base::FD_ANY, kClientAddr1);
+  AddInterface(talk_base::SocketAddress(kClientAddrHost1, kClientAddrPort));
+  AddInterface(talk_base::SocketAddress(kClientAddrHost2, kClientAddrPort));
+  fss_->AddRule(false,
+                talk_base::FP_UDP,
+                talk_base::FD_ANY,
+                talk_base::SocketAddress(kClientAddrHost1, kClientAddrPort));
   Init(NULL);
   mediastream_signaling_.SendAudioVideoStream1();
   InitiateCall();
-  // Since kClientAddr1 is blocked, not expecting stun candidates for it.
+  // Since kClientAddrHost1 is blocked, not expecting stun candidates for it.
   EXPECT_TRUE_WAIT(observer_.oncandidatesready_, kIceCandidatesTimeout);
   EXPECT_EQ(6u, observer_.mline_0_candidates_.size());
   EXPECT_EQ(6u, observer_.mline_1_candidates_.size());
@@ -1420,7 +1436,7 @@
 // Test that local candidates are added to the local session description and
 // that they are retained if the local session description is changed.
 TEST_F(WebRtcSessionTest, TestLocalCandidatesAddedToSessionDescription) {
-  AddInterface(kClientAddr1);
+  AddInterface(talk_base::SocketAddress(kClientAddrHost1, kClientAddrPort));
   Init(NULL);
   mediastream_signaling_.SendAudioVideoStream1();
   CreateAndSetRemoteOfferAndLocalAnswer();
@@ -1484,7 +1500,7 @@
 // Test that offers and answers contains ice candidates when Ice candidates have
 // been gathered.
 TEST_F(WebRtcSessionTest, TestSetLocalAndRemoteDescriptionWithCandidates) {
-  AddInterface(kClientAddr1);
+  AddInterface(talk_base::SocketAddress(kClientAddrHost1, kClientAddrPort));
   Init(NULL);
   mediastream_signaling_.SendAudioVideoStream1();
   // Ice is started but candidates are not provided until SetLocalDescription
diff --git a/app/webrtc/webrtcsessiondescriptionfactory.cc b/app/webrtc/webrtcsessiondescriptionfactory.cc
index 51427d2..b6f523c 100644
--- a/app/webrtc/webrtcsessiondescriptionfactory.cc
+++ b/app/webrtc/webrtcsessiondescriptionfactory.cc
@@ -33,10 +33,10 @@
 #include "talk/app/webrtc/mediastreamsignaling.h"
 #include "talk/app/webrtc/webrtcsession.h"
 
+using cricket::MediaSessionOptions;
+
 namespace webrtc {
-
 namespace {
-
 static const char kFailedDueToIdentityFailed[] =
     " failed because DTLS identity request failed";
 
@@ -46,25 +46,24 @@
 
 static const uint64 kInitSessionVersion = 2;
 
-typedef cricket::MediaSessionOptions::Stream Stream;
-typedef cricket::MediaSessionOptions::Streams Streams;
-
-static bool CompareStream(const Stream& stream1, const Stream& stream2) {
-  return (stream1.id < stream2.id);
+static bool CompareStream(const MediaSessionOptions::Stream& stream1,
+                          const MediaSessionOptions::Stream& stream2) {
+  return stream1.id < stream2.id;
 }
 
-static bool SameId(const Stream& stream1, const Stream& stream2) {
-  return (stream1.id == stream2.id);
+static bool SameId(const MediaSessionOptions::Stream& stream1,
+                   const MediaSessionOptions::Stream& stream2) {
+  return stream1.id == stream2.id;
 }
 
 // Checks if each Stream within the |streams| has unique id.
-static bool ValidStreams(const Streams& streams) {
-  Streams sorted_streams = streams;
+static bool ValidStreams(const MediaSessionOptions::Streams& streams) {
+  MediaSessionOptions::Streams sorted_streams = streams;
   std::sort(sorted_streams.begin(), sorted_streams.end(), CompareStream);
-  Streams::iterator it =
+  MediaSessionOptions::Streams::iterator it =
       std::adjacent_find(sorted_streams.begin(), sorted_streams.end(),
                          SameId);
-  return (it == sorted_streams.end());
+  return it == sorted_streams.end();
 }
 
 enum {
@@ -83,7 +82,6 @@
   std::string error;
   talk_base::scoped_ptr<webrtc::SessionDescriptionInterface> description;
 };
-
 }  // namespace
 
 // static
@@ -130,33 +128,35 @@
   transport_desc_factory_.set_protocol(cricket::ICEPROTO_HYBRID);
   session_desc_factory_.set_add_legacy_streams(false);
   // By default SRTP-SDES is enabled in WebRtc.
-  set_secure(cricket::SEC_REQUIRED);
+  SetSecure(cricket::SEC_REQUIRED);
 
-  if (dtls_enabled) {
-    if (identity_service_.get()) {
-      identity_request_observer_ =
-        new talk_base::RefCountedObject<WebRtcIdentityRequestObserver>();
+  if (!dtls_enabled) {
+    return;
+  }
 
-      identity_request_observer_->SignalRequestFailed.connect(
-          this, &WebRtcSessionDescriptionFactory::OnIdentityRequestFailed);
-      identity_request_observer_->SignalIdentityReady.connect(
-          this, &WebRtcSessionDescriptionFactory::OnIdentityReady);
+  if (identity_service_.get()) {
+    identity_request_observer_ =
+      new talk_base::RefCountedObject<WebRtcIdentityRequestObserver>();
 
-      if (identity_service_->RequestIdentity(kWebRTCIdentityName,
-                                             kWebRTCIdentityName,
-                                             identity_request_observer_)) {
-        LOG(LS_VERBOSE) << "DTLS-SRTP enabled; sent DTLS identity request.";
-        identity_request_state_ = IDENTITY_WAITING;
-      } else {
-        LOG(LS_ERROR) << "Failed to send DTLS identity request.";
-        identity_request_state_ = IDENTITY_FAILED;
-      }
-    } else {
+    identity_request_observer_->SignalRequestFailed.connect(
+        this, &WebRtcSessionDescriptionFactory::OnIdentityRequestFailed);
+    identity_request_observer_->SignalIdentityReady.connect(
+        this, &WebRtcSessionDescriptionFactory::OnIdentityReady);
+
+    if (identity_service_->RequestIdentity(kWebRTCIdentityName,
+                                           kWebRTCIdentityName,
+                                           identity_request_observer_)) {
+      LOG(LS_VERBOSE) << "DTLS-SRTP enabled; sent DTLS identity request.";
       identity_request_state_ = IDENTITY_WAITING;
-      // Do not generate the identity in the constructor since the caller has
-      // not got a chance to connect to SignalIdentityReady.
-      signaling_thread_->Post(this, MSG_GENERATE_IDENTITY, NULL);
+    } else {
+      LOG(LS_ERROR) << "Failed to send DTLS identity request.";
+      identity_request_state_ = IDENTITY_FAILED;
     }
+  } else {
+    identity_request_state_ = IDENTITY_WAITING;
+    // Do not generate the identity in the constructor since the caller has
+    // not got a chance to connect to SignalIdentityReady.
+    signaling_thread_->Post(this, MSG_GENERATE_IDENTITY, NULL);
   }
 }
 
@@ -261,19 +261,15 @@
   }
 }
 
-void WebRtcSessionDescriptionFactory::set_secure(
+void WebRtcSessionDescriptionFactory::SetSecure(
     cricket::SecureMediaPolicy secure_policy) {
   session_desc_factory_.set_secure(secure_policy);
 }
 
-cricket::SecureMediaPolicy WebRtcSessionDescriptionFactory::secure() const {
+cricket::SecureMediaPolicy WebRtcSessionDescriptionFactory::Secure() const {
   return session_desc_factory_.secure();
 }
 
-bool WebRtcSessionDescriptionFactory::waiting_for_identity() const {
-  return identity_request_state_ == IDENTITY_WAITING;
-}
-
 void WebRtcSessionDescriptionFactory::OnMessage(talk_base::Message* msg) {
   switch (msg->message_id) {
     case MSG_CREATE_SESSIONDESCRIPTION_SUCCESS: {
@@ -450,5 +446,4 @@
     create_session_description_requests_.pop();
   }
 }
-
 }  // namespace webrtc
diff --git a/app/webrtc/webrtcsessiondescriptionfactory.h b/app/webrtc/webrtcsessiondescriptionfactory.h
index ba34e91..ca614b4 100644
--- a/app/webrtc/webrtcsessiondescriptionfactory.h
+++ b/app/webrtc/webrtcsessiondescriptionfactory.h
@@ -34,21 +34,17 @@
 #include "talk/session/media/mediasession.h"
 
 namespace cricket {
-
 class ChannelManager;
 class TransportDescriptionFactory;
-
 }  // namespace cricket
 
 namespace webrtc {
-
 class CreateSessionDescriptionObserver;
 class MediaConstraintsInterface;
 class MediaStreamSignaling;
 class SessionDescriptionInterface;
 class WebRtcSession;
 
-
 // DTLS identity request callback class.
 class WebRtcIdentityRequestObserver : public DTLSIdentityRequestObserver,
                                       public sigslot::has_slots<> {
@@ -116,13 +112,15 @@
       CreateSessionDescriptionObserver* observer,
       const MediaConstraintsInterface* constraints);
 
-  void set_secure(cricket::SecureMediaPolicy secure_policy);
-  cricket::SecureMediaPolicy secure() const;
+  void SetSecure(cricket::SecureMediaPolicy secure_policy);
+  cricket::SecureMediaPolicy Secure() const;
 
   sigslot::signal1<talk_base::SSLIdentity*> SignalIdentityReady;
 
   // For testing.
-  bool waiting_for_identity() const;
+  bool waiting_for_identity() const {
+    return identity_request_state_ == IDENTITY_WAITING;
+  }
 
  private:
   enum IdentityRequestState {
@@ -166,7 +164,6 @@
 
   DISALLOW_COPY_AND_ASSIGN(WebRtcSessionDescriptionFactory);
 };
-
 }  // namespace webrtc
 
 #endif  // TALK_APP_WEBRTC_WEBRTCSESSIONDESCRIPTIONFACTORY_H_
diff --git a/libjingle_examples.gyp b/libjingle_examples.gyp
index a22daf1..c69aa9e 100755
--- a/libjingle_examples.gyp
+++ b/libjingle_examples.gyp
@@ -261,35 +261,6 @@
               '-framework UIKit',
             ],
           },
-          'postbuilds': [
-            {
-              # Ideally app signing would be a part of gyp.
-              # Delete if/when that comes to pass.
-              'postbuild_name': 'Sign AppRTCDemo',
-              'variables': {
-                'variables': {
-                  'key_id%': '<!(security find-identity -p codesigning -v | grep "iPhone Developer" | awk \'{print $2}\')',
-                },
-                'key_id%': '<(key_id)',
-                # Total HACK to give a more informative message when multiple
-                # codesigning keys are present in the default keychain.  Ideally
-                # we could pick more intelligently among the keys, but as a
-                # first cut just tell the developer to specify a key identity
-                # explicitly.
-                'ensure_single_key': '<!(python -c "assert \'\\n\' not in \'\'\'<(key_id)\'\'\', \'key_id gyp variable needs to be set explicitly because there are multiple codesigning keys!\'")',
-              },
-              'conditions': [
-                ['key_id==""', {
-                  'action': [ 'echo', 'Skipping signing' ],
-                }, {
-                  'action': [
-                    '/usr/bin/codesign', '-v', '--force', '--sign', '<(key_id)',
-                    '${BUILT_PRODUCTS_DIR}/AppRTCDemo.app',
-                  ],
-                }],
-              ],
-            },
-          ],
         },  # target AppRTCDemo
       ],  # targets
     }],  # OS=="ios"
diff --git a/libjingle_tests.gyp b/libjingle_tests.gyp
index 44fa8f7..a88942f 100755
--- a/libjingle_tests.gyp
+++ b/libjingle_tests.gyp
@@ -383,6 +383,7 @@
         # 'app/webrtc/mediastreamhandler_unittest.cc',
         'app/webrtc/mediastreamsignaling_unittest.cc',
         'app/webrtc/peerconnection_unittest.cc',
+        'app/webrtc/peerconnectionendtoend_unittest.cc',
         'app/webrtc/peerconnectionfactory_unittest.cc',
         'app/webrtc/peerconnectioninterface_unittest.cc',
         # 'app/webrtc/peerconnectionproxy_unittest.cc',
@@ -397,6 +398,8 @@
         'app/webrtc/test/fakeperiodicvideocapturer.h',
         'app/webrtc/test/fakevideotrackrenderer.h',
         'app/webrtc/test/mockpeerconnectionobservers.h',
+        'app/webrtc/test/peerconnectiontestwrapper.h',
+        'app/webrtc/test/peerconnectiontestwrapper.cc',
         'app/webrtc/test/testsdpstrings.h',
         'app/webrtc/videosource_unittest.cc',
         'app/webrtc/videotrack_unittest.cc',
diff --git a/media/base/videoadapter.h b/media/base/videoadapter.h
index 2bd31d5..38a8c9d 100644
--- a/media/base/videoadapter.h
+++ b/media/base/videoadapter.h
@@ -109,7 +109,7 @@
     : public VideoAdapter, public sigslot::has_slots<>  {
  public:
   enum AdaptRequest { UPGRADE, KEEP, DOWNGRADE };
-  enum {
+  enum AdaptReasonEnum {
     ADAPTREASON_CPU = 1,
     ADAPTREASON_BANDWIDTH = 2,
     ADAPTREASON_VIEW = 4
diff --git a/media/webrtc/fakewebrtcvoiceengine.h b/media/webrtc/fakewebrtcvoiceengine.h
index 9696518..4ecefff 100644
--- a/media/webrtc/fakewebrtcvoiceengine.h
+++ b/media/webrtc/fakewebrtcvoiceengine.h
@@ -252,6 +252,19 @@
                                 true);
     }
   }
+  int AddChannel() {
+    if (fail_create_channel_) {
+      return -1;
+    }
+    Channel* ch = new Channel();
+    for (int i = 0; i < NumOfCodecs(); ++i) {
+      webrtc::CodecInst codec;
+      GetCodec(i, codec);
+      ch->recv_codecs.push_back(codec);
+    }
+    channels_[++last_channel_] = ch;
+    return last_channel_;
+  }
 
   WEBRTC_STUB(Release, ());
 
@@ -275,18 +288,13 @@
     return NULL;
   }
   WEBRTC_FUNC(CreateChannel, ()) {
-    if (fail_create_channel_) {
-      return -1;
-    }
-    Channel* ch = new Channel();
-    for (int i = 0; i < NumOfCodecs(); ++i) {
-      webrtc::CodecInst codec;
-      GetCodec(i, codec);
-      ch->recv_codecs.push_back(codec);
-    }
-    channels_[++last_channel_] = ch;
-    return last_channel_;
+    return AddChannel();
   }
+#ifdef USE_WEBRTC_DEV_BRANCH
+  WEBRTC_FUNC(CreateChannel, (const webrtc::Config& /*config*/)) {
+    return AddChannel();
+  }
+#endif
   WEBRTC_FUNC(DeleteChannel, (int channel)) {
     WEBRTC_CHECK_CHANNEL(channel);
     delete channels_[channel];
diff --git a/p2p/base/session.cc b/p2p/base/session.cc
index e5c86c1..e0f8dd3 100644
--- a/p2p/base/session.cc
+++ b/p2p/base/session.cc
@@ -998,10 +998,11 @@
   return true;
 }
 
-bool Session::SendInfoMessage(const XmlElements& elems) {
+bool Session::SendInfoMessage(const XmlElements& elems,
+                              const std::string& remote_name) {
   ASSERT(signaling_thread()->IsCurrent());
   SessionError error;
-  if (!SendMessage(ACTION_SESSION_INFO, elems, &error)) {
+  if (!SendMessage(ACTION_SESSION_INFO, elems, remote_name, &error)) {
     LOG(LS_ERROR) << "Could not send info message " << error.text;
     return false;
   }
@@ -1644,11 +1645,16 @@
 
 bool Session::SendMessage(ActionType type, const XmlElements& action_elems,
                           SessionError* error) {
+    return SendMessage(type, action_elems, remote_name(), error);
+}
+
+bool Session::SendMessage(ActionType type, const XmlElements& action_elems,
+                          const std::string& remote_name, SessionError* error) {
   talk_base::scoped_ptr<buzz::XmlElement> stanza(
       new buzz::XmlElement(buzz::QN_IQ));
 
   SessionMessage msg(current_protocol_, type, id(), initiator_name());
-  msg.to = remote_name();
+  msg.to = remote_name;
   WriteSessionMessage(msg, action_elems, stanza.get());
 
   SignalOutgoingMessage(this, stanza.get());
diff --git a/p2p/base/session.h b/p2p/base/session.h
index 292e7a5..637c942 100644
--- a/p2p/base/session.h
+++ b/p2p/base/session.h
@@ -584,7 +584,8 @@
   // arbitrary XML messages, which are called "info" messages. Sending
   // takes ownership of the given elements.  The signal does not; the
   // parent element will be deleted after the signal.
-  bool SendInfoMessage(const XmlElements& elems);
+  bool SendInfoMessage(const XmlElements& elems,
+                       const std::string& remote_name);
   bool SendDescriptionInfoMessage(const ContentInfos& contents);
   sigslot::signal2<Session*, const buzz::XmlElement*> SignalInfoMessage;
 
@@ -638,7 +639,7 @@
   bool ResendAllTransportInfoMessages(SessionError* error);
   bool SendAllUnsentTransportInfoMessages(SessionError* error);
 
-  // Both versions of SendMessage send a message of the given type to
+  // All versions of SendMessage send a message of the given type to
   // the other client.  Can pass either a set of elements or an
   // "action", which must have a WriteSessionAction method to go along
   // with it.  Sending with an action supports sending a "hybrid"
@@ -648,6 +649,10 @@
   // Takes ownership of action_elems.
   bool SendMessage(ActionType type, const XmlElements& action_elems,
                    SessionError* error);
+  // Sends a messge, but overrides the remote name.
+  bool SendMessage(ActionType type, const XmlElements& action_elems,
+                   const std::string& remote_name,
+                   SessionError* error);
   // When passing an action, may be Hybrid protocol.
   template <typename Action>
   bool SendMessage(ActionType type, const Action& action,
diff --git a/session/media/call.cc b/session/media/call.cc
index 51a721b..967846b 100644
--- a/session/media/call.cc
+++ b/session/media/call.cc
@@ -186,7 +186,7 @@
     return false;
   }
 
-  return session->SendInfoMessage(elems);
+  return session->SendInfoMessage(elems, session->remote_name());
 }
 
 void Call::SetLocalRenderer(VideoRenderer* renderer) {