Update stable to r4872.

git-svn-id: http://webrtc.googlecode.com/svn/stable/talk@4873 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/app/webrtc/webrtcsession.cc b/app/webrtc/webrtcsession.cc
index f1fb40d..83f8912 100644
--- a/app/webrtc/webrtcsession.cc
+++ b/app/webrtc/webrtcsession.cc
@@ -54,7 +54,7 @@
 
 namespace webrtc {
 
-const char kInternalConstraintPrefix[] = "internal";
+const char MediaConstraintsInterface::kInternalConstraintPrefix[] = "internal";
 
 // Supported MediaConstraints.
 // DTLS-SRTP pseudo-constraints.
@@ -81,6 +81,8 @@
     "Offer and answer descriptions m-lines are not matching. "
     "Rejecting answer.";
 const char kSdpWithoutCrypto[] = "Called with a SDP without crypto enabled.";
+const char kSdpWithoutSdesAndDtlsDisabled[] =
+    "Called with a SDP without SDES crypto and DTLS disabled locally.";
 const char kSessionError[] = "Session error code: ";
 const char kUpdateStateFailed[] = "Failed to update session state: ";
 const char kPushDownOfferTDFailed[] =
@@ -110,10 +112,9 @@
 // fingerprint. Mismatches, such as replying with a DTLS fingerprint to SDES
 // keys, will be caught in Transport negotiation, and backstopped by Channel's
 // |secure_required| check.
-static bool VerifyCrypto(const SessionDescription* desc) {
-  if (!desc) {
-    return false;
-  }
+static bool VerifyCrypto(const SessionDescription* desc,
+                         bool dtls_enabled,
+                         std::string* error) {
   const ContentInfos& contents = desc->contents();
   for (size_t index = 0; index < contents.size(); ++index) {
     const ContentInfo* cinfo = &contents[index];
@@ -128,13 +129,22 @@
     if (!media || !tinfo) {
       // Something is not right.
       LOG(LS_ERROR) << kInvalidSdp;
+      *error = kInvalidSdp;
       return false;
     }
-    if (media->cryptos().empty() &&
-        !tinfo->description.identity_fingerprint) {
-      // Crypto must be supplied.
-      LOG(LS_WARNING) << "Session description must have SDES or DTLS-SRTP.";
-      return false;
+    if (media->cryptos().empty()) {
+      if (!tinfo->description.identity_fingerprint) {
+        // Crypto must be supplied.
+        LOG(LS_WARNING) << "Session description must have SDES or DTLS-SRTP.";
+        *error = kSdpWithoutCrypto;
+        return false;
+      }
+      if (!dtls_enabled) {
+        LOG(LS_WARNING) <<
+            "Session description must have SDES when DTLS disabled.";
+        *error = kSdpWithoutSdesAndDtlsDisabled;
+        return false;
+      }
     }
   }
 
@@ -392,6 +402,7 @@
       ice_observer_(NULL),
       ice_connection_state_(PeerConnectionInterface::kIceConnectionNew),
       older_version_remote_peer_(false),
+      dtls_enabled_(false),
       data_channel_type_(cricket::DCT_NONE),
       ice_restart_latch_(new IceRestartAnswerLatch) {
 }
@@ -424,13 +435,13 @@
   bool value;
 
   // Enable DTLS by default if |dtls_identity_service| is valid.
-  bool dtls_enabled = (dtls_identity_service != NULL);
-  // |constraints| can override the default |dtls_enabled| value.
+  dtls_enabled_ = (dtls_identity_service != NULL);
+  // |constraints| can override the default |dtls_enabled_| value.
   if (FindConstraint(
         constraints,
         MediaConstraintsInterface::kEnableDtlsSrtp,
         &value, NULL)) {
-    dtls_enabled = value;
+    dtls_enabled_ = value;
   }
 
   // Enable creation of RTP data channels if the kEnableRtpDataChannels is set.
@@ -446,7 +457,7 @@
         MediaConstraintsInterface::kEnableSctpDataChannels,
         &value, NULL) && value;
     // DTLS has to be enabled to use SCTP.
-    if (sctp_enabled && dtls_enabled) {
+    if (sctp_enabled && dtls_enabled_) {
       LOG(LS_INFO) << "Allowing SCTP data engine.";
       data_channel_type_ = cricket::DCT_SCTP;
     }
@@ -473,7 +484,7 @@
       this,
       id(),
       data_channel_type_,
-      dtls_enabled));
+      dtls_enabled_));
 
   webrtc_session_desc_factory_->SignalIdentityReady.connect(
       this, &WebRtcSession::OnIdentityReady);
@@ -1383,9 +1394,10 @@
   }
 
   // Verify crypto settings.
+  std::string crypto_error;
   if (webrtc_session_desc_factory_->secure() == cricket::SEC_REQUIRED &&
-      !VerifyCrypto(sdesc->description())) {
-    return BadSdp(source, kSdpWithoutCrypto, error_desc);
+      !VerifyCrypto(sdesc->description(), dtls_enabled_, &crypto_error)) {
+    return BadSdp(source, crypto_error, error_desc);
   }
 
   if (!ValidateBundleSettings(sdesc->description())) {
diff --git a/app/webrtc/webrtcsession.h b/app/webrtc/webrtcsession.h
index 0cb049f..eee579d 100644
--- a/app/webrtc/webrtcsession.h
+++ b/app/webrtc/webrtcsession.h
@@ -68,6 +68,7 @@
 extern const char kInvalidSdp[];
 extern const char kMlineMismatch[];
 extern const char kSdpWithoutCrypto[];
+extern const char kSdpWithoutSdesAndDtlsDisabled[];
 extern const char kSessionError[];
 extern const char kUpdateStateFailed[];
 extern const char kPushDownOfferTDFailed[];
@@ -299,6 +300,7 @@
   std::vector<IceCandidateInterface*> saved_candidates_;
   // If the remote peer is using a older version of implementation.
   bool older_version_remote_peer_;
+  bool dtls_enabled_;
   // Specifies which kind of data channel is allowed. This is controlled
   // by the chrome command-line flag and constraints:
   // 1. If chrome command-line switch 'enable-sctp-data-channels' is enabled,
diff --git a/app/webrtc/webrtcsession_unittest.cc b/app/webrtc/webrtcsession_unittest.cc
index 1531e0e..acbc924 100644
--- a/app/webrtc/webrtcsession_unittest.cc
+++ b/app/webrtc/webrtcsession_unittest.cc
@@ -86,6 +86,7 @@
 using webrtc::WebRtcSession;
 using webrtc::kMlineMismatch;
 using webrtc::kSdpWithoutCrypto;
+using webrtc::kSdpWithoutSdesAndDtlsDisabled;
 using webrtc::kSessionError;
 using webrtc::kSetLocalSdpFailed;
 using webrtc::kSetRemoteSdpFailed;
@@ -114,6 +115,10 @@
 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";
+
 // Add some extra |newlines| to the |message| after |line|.
 static void InjectAfter(const std::string& line,
                         const std::string& newlines,
@@ -2629,6 +2634,28 @@
   VerifyMultipleAsyncCreateDescription(
       false, CreateSessionDescriptionRequest::kAnswer);
 }
+
+// Verifies that setRemoteDescription fails when DTLS is disabled and the remote
+// offer has no SDES crypto but only DTLS fingerprint.
+TEST_F(WebRtcSessionTest, TestSetRemoteOfferFailIfDtlsDisabledAndNoCrypto) {
+  // Init without DTLS.
+  Init(NULL);
+  // Create a remote offer with secured transport disabled.
+  cricket::MediaSessionOptions options;
+  JsepSessionDescription* offer(CreateRemoteOffer(
+      options, cricket::SEC_DISABLED));
+  // Adds a DTLS fingerprint to the remote offer.
+  cricket::SessionDescription* sdp = offer->description();
+  TransportInfo* audio = sdp->GetTransportInfoByName("audio");
+  ASSERT_TRUE(audio != NULL);
+  ASSERT_TRUE(audio->description.identity_fingerprint.get() == NULL);
+  audio->description.identity_fingerprint.reset(
+      talk_base::SSLFingerprint::CreateFromRfc4572(
+          talk_base::DIGEST_SHA_256, kFakeDtlsFingerprint));
+  SetRemoteDescriptionExpectError(kSdpWithoutSdesAndDtlsDisabled,
+                                  offer);
+}
+
 // TODO(bemasc): Add a TestIceStatesBundle with BUNDLE enabled.  That test
 // currently fails because upon disconnection and reconnection OnIceComplete is
 // called more than once without returning to IceGatheringGathering.
diff --git a/base/linuxwindowpicker_unittest.cc b/base/linuxwindowpicker_unittest.cc
index 5ea9c93..c248bba 100644
--- a/base/linuxwindowpicker_unittest.cc
+++ b/base/linuxwindowpicker_unittest.cc
@@ -28,6 +28,7 @@
 #include "talk/base/gunit.h"
 #include "talk/base/linuxwindowpicker.h"
 #include "talk/base/logging.h"
+#include "talk/base/testutils.h"
 #include "talk/base/windowpicker.h"
 
 #ifndef LINUX
@@ -37,6 +38,7 @@
 namespace talk_base {
 
 TEST(LinuxWindowPickerTest, TestGetWindowList) {
+  MAYBE_SKIP_SCREENCAST_TEST();
   LinuxWindowPicker window_picker;
   WindowDescriptionList descriptions;
   window_picker.Init();
@@ -44,6 +46,7 @@
 }
 
 TEST(LinuxWindowPickerTest, TestGetDesktopList) {
+  MAYBE_SKIP_SCREENCAST_TEST();
   LinuxWindowPicker window_picker;
   DesktopDescriptionList descriptions;
   EXPECT_TRUE(window_picker.Init());
diff --git a/base/network.cc b/base/network.cc
index d6367c3..cbfcb5f 100644
--- a/base/network.cc
+++ b/base/network.cc
@@ -32,10 +32,18 @@
 #include "talk/base/network.h"
 
 #ifdef POSIX
+// linux/if.h can't be included at the same time as the posix sys/if.h, and
+// it's transitively required by linux/route.h, so include that version on
+// linux instead of the standard posix one.
+#if defined(ANDROID) || defined(LINUX)
+#include <linux/if.h>
+#include <linux/route.h>
+#else
+#include <net/if.h>
+#endif
 #include <sys/socket.h>
 #include <sys/utsname.h>
 #include <sys/ioctl.h>
-#include <net/if.h>
 #include <unistd.h>
 #include <errno.h>
 #ifdef ANDROID
@@ -173,7 +181,8 @@
 }
 
 BasicNetworkManager::BasicNetworkManager()
-    : thread_(NULL), sent_first_update_(false), start_count_(0) {
+    : thread_(NULL), sent_first_update_(false), start_count_(0),
+      ignore_non_default_routes_(false) {
 }
 
 BasicNetworkManager::~BasicNetworkManager() {
@@ -397,14 +406,52 @@
 }
 #endif  // WIN32
 
-bool BasicNetworkManager::IsIgnoredNetwork(const Network& network) {
+#if defined(ANDROID) || defined(LINUX)
+bool IsDefaultRoute(const std::string& network_name) {
+  FileStream fs;
+  if (!fs.Open("/proc/net/route", "r", NULL)) {
+    LOG(LS_WARNING) << "Couldn't read /proc/net/route, skipping default "
+                    << "route check (assuming everything is a default route).";
+    return true;
+  } else {
+    std::string line;
+    while (fs.ReadLine(&line) == SR_SUCCESS) {
+      char iface_name[256];
+      unsigned int iface_ip, iface_gw, iface_mask, iface_flags;
+      if (sscanf(line.c_str(),
+                 "%255s %8X %8X %4X %*d %*u %*d %8X",
+                 iface_name, &iface_ip, &iface_gw,
+                 &iface_flags, &iface_mask) == 5 &&
+          network_name == iface_name &&
+          iface_mask == 0 &&
+          (iface_flags & (RTF_UP | RTF_HOST)) == RTF_UP) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+#endif
+
+bool BasicNetworkManager::IsIgnoredNetwork(const Network& network) const {
+  // Ignore networks on the explicit ignore list.
+  for (size_t i = 0; i < network_ignore_list_.size(); ++i) {
+    if (network.name() == network_ignore_list_[i]) {
+      return true;
+    }
+  }
 #ifdef POSIX
-  // Ignore local networks (lo, lo0, etc)
-  // Also filter out VMware interfaces, typically named vmnet1 and vmnet8
+  // Filter out VMware interfaces, typically named vmnet1 and vmnet8
   if (strncmp(network.name().c_str(), "vmnet", 5) == 0 ||
       strncmp(network.name().c_str(), "vnic", 4) == 0) {
     return true;
   }
+#if defined(ANDROID) || defined(LINUX)
+  // Make sure this is a default route, if we're ignoring non-defaults.
+  if (ignore_non_default_routes_ && !IsDefaultRoute(network.name())) {
+    return true;
+  }
+#endif
 #elif defined(WIN32)
   // Ignore any HOST side vmware adapters with a description like:
   // VMware Virtual Ethernet Adapter for VMnet1
diff --git a/base/network.h b/base/network.h
index f87063d..63f3e73 100644
--- a/base/network.h
+++ b/base/network.h
@@ -127,6 +127,18 @@
   virtual void OnMessage(Message* msg);
   bool started() { return start_count_ > 0; }
 
+  // Sets the network ignore list, which is empty by default. Any network on
+  // the ignore list will be filtered from network enumeration results.
+  void set_network_ignore_list(const std::vector<std::string>& list) {
+    network_ignore_list_ = list;
+  }
+#if defined(ANDROID) || defined(LINUX)
+  // Sets the flag for ignoring non-default routes.
+  void set_ignore_non_default_routes(bool value) {
+    ignore_non_default_routes_ = true;
+  }
+#endif
+
  protected:
 #if defined(POSIX)
   // Separated from CreateNetworks for tests.
@@ -139,7 +151,7 @@
   bool CreateNetworks(bool include_ignored, NetworkList* networks) const;
 
   // Determines if a network should be ignored.
-  static bool IsIgnoredNetwork(const Network& network);
+  bool IsIgnoredNetwork(const Network& network) const;
 
  private:
   friend class NetworkTest;
@@ -149,6 +161,8 @@
   Thread* thread_;
   bool sent_first_update_;
   int start_count_;
+  std::vector<std::string> network_ignore_list_;
+  bool ignore_non_default_routes_;
 };
 
 // Represents a Unix-type network interface, with a name and single address.
diff --git a/base/network_unittest.cc b/base/network_unittest.cc
index ee0f0aa..d12a1eb 100644
--- a/base/network_unittest.cc
+++ b/base/network_unittest.cc
@@ -54,8 +54,9 @@
     network_manager.MergeNetworkList(list, changed);
   }
 
-  bool IsIgnoredNetwork(const Network& network) {
-    return BasicNetworkManager::IsIgnoredNetwork(network);
+  bool IsIgnoredNetwork(BasicNetworkManager& network_manager,
+                        const Network& network) {
+    return network_manager.IsIgnoredNetwork(network);
   }
 
   NetworkManager::NetworkList GetNetworks(
@@ -96,8 +97,24 @@
                         IPAddress(0x12345600U), 24);
   Network ipv4_network2("test_eth1", "Test Network Adapter 2",
                         IPAddress(0x00010000U), 16);
-  EXPECT_FALSE(IsIgnoredNetwork(ipv4_network1));
-  EXPECT_TRUE(IsIgnoredNetwork(ipv4_network2));
+  BasicNetworkManager network_manager;
+  EXPECT_FALSE(IsIgnoredNetwork(network_manager, ipv4_network1));
+  EXPECT_TRUE(IsIgnoredNetwork(network_manager, ipv4_network2));
+}
+
+TEST_F(NetworkTest, TestIgnoreList) {
+  Network ignore_me("ignore_me", "Ignore me please!",
+                    IPAddress(0x12345600U), 24);
+  Network include_me("include_me", "Include me please!",
+                     IPAddress(0x12345600U), 24);
+  BasicNetworkManager network_manager;
+  EXPECT_FALSE(IsIgnoredNetwork(network_manager, ignore_me));
+  EXPECT_FALSE(IsIgnoredNetwork(network_manager, include_me));
+  std::vector<std::string> ignore_list;
+  ignore_list.push_back("ignore_me");
+  network_manager.set_network_ignore_list(ignore_list);
+  EXPECT_TRUE(IsIgnoredNetwork(network_manager, ignore_me));
+  EXPECT_FALSE(IsIgnoredNetwork(network_manager, include_me));
 }
 
 TEST_F(NetworkTest, TestCreateNetworks) {
diff --git a/base/openssladapter.cc b/base/openssladapter.cc
index af92f0c..50391e5 100644
--- a/base/openssladapter.cc
+++ b/base/openssladapter.cc
@@ -233,10 +233,7 @@
 bool OpenSSLAdapter::InitializeSSL(VerificationCallback callback) {
   if (!InitializeSSLThread() || !SSL_library_init())
       return false;
-#if !defined(ADDRESS_SANITIZER) || !defined(OSX)
-  // Loading the error strings crashes mac_asan.  Omit this debugging aid there.
   SSL_load_error_strings();
-#endif
   ERR_load_BIO_strings();
   OpenSSL_add_all_algorithms();
   RAND_poll();
diff --git a/base/testutils.h b/base/testutils.h
index 769d95f..e8ad720 100644
--- a/base/testutils.h
+++ b/base/testutils.h
@@ -30,6 +30,13 @@
 
 // Utilities for testing talk_base infrastructure in unittests
 
+#ifdef LINUX
+#include <X11/Xlib.h>
+// X defines a few macros that stomp on types that gunit.h uses.
+#undef None
+#undef Bool
+#endif
+
 #include <map>
 #include <vector>
 #include "talk/base/asyncsocket.h"
@@ -565,6 +572,38 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+// Helpers for determining if X/screencasting is available (on linux).
+
+#define MAYBE_SKIP_SCREENCAST_TEST() \
+  if (!testing::IsScreencastingAvailable()) { \
+    LOG(LS_WARNING) << "Skipping test, since it doesn't have the requisite " \
+                    << "X environment for screen capture."; \
+    return; \
+  } \
+
+#ifdef LINUX
+struct XDisplay {
+  XDisplay() : display_(XOpenDisplay(NULL)) { }
+  ~XDisplay() { if (display_) XCloseDisplay(display_); }
+  bool IsValid() const { return display_ != NULL; }
+  operator Display*() { return display_; }
+ private:
+  Display* display_;
+};
+#endif
+
+// Returns true if screencasting is available. When false, anything that uses
+// screencasting features may fail.
+inline bool IsScreencastingAvailable() {
+#ifdef LINUX
+  XDisplay display;
+  if (!display.IsValid()) {
+    LOG(LS_WARNING) << "No X Display available.";
+    return false;
+  }
+#endif
+  return true;
+}
 }  // namespace testing
 
 #endif  // TALK_BASE_TESTUTILS_H__
diff --git a/base/thread.cc b/base/thread.cc
index d21d5f1..0c43e63 100644
--- a/base/thread.cc
+++ b/base/thread.cc
@@ -146,7 +146,6 @@
     : MessageQueue(ss),
       priority_(PRIORITY_NORMAL),
       started_(false),
-      has_sends_(false),
 #if defined(WIN32)
       thread_(NULL),
       thread_id_(0),
@@ -405,7 +404,6 @@
     smsg.msg = msg;
     smsg.ready = &ready;
     sendlist_.push_back(smsg);
-    has_sends_ = true;
   }
 
   // Wait for a reply
@@ -436,11 +434,6 @@
 }
 
 void Thread::ReceiveSends() {
-  // Before entering critical section, check boolean.
-
-  if (!has_sends_)
-    return;
-
   // Receive a sent message. Cleanup scenarios:
   // - thread sending exits: We don't allow this, since thread can exit
   //   only via Join, so Send must complete.
@@ -456,7 +449,6 @@
     *smsg.ready = true;
     smsg.thread->socketserver()->WakeUp();
   }
-  has_sends_ = false;
   crit_.Leave();
 }
 
diff --git a/base/thread.h b/base/thread.h
index 55ec0da..e679ea4 100644
--- a/base/thread.h
+++ b/base/thread.h
@@ -255,7 +255,6 @@
   std::string name_;
   ThreadPriority priority_;
   bool started_;
-  bool has_sends_;
 
 #ifdef POSIX
   pthread_t thread_;
diff --git a/base/virtualsocket_unittest.cc b/base/virtualsocket_unittest.cc
index 7f233df..7bbb5f0 100644
--- a/base/virtualsocket_unittest.cc
+++ b/base/virtualsocket_unittest.cc
@@ -46,7 +46,7 @@
   Sender(Thread* th, AsyncSocket* s, uint32 rt)
       : thread(th), socket(new AsyncUDPSocket(s)),
         done(false), rate(rt), count(0) {
-    last_send = Time();
+    last_send = talk_base::Time();
     thread->PostDelayed(NextDelay(), this, 1);
   }
 
@@ -61,7 +61,7 @@
     if (done)
       return;
 
-    uint32 cur_time = Time();
+    uint32 cur_time = talk_base::Time();
     uint32 delay = cur_time - last_send;
     uint32 size = rate * delay / 1000;
     size = std::min<uint32>(size, 4096);
@@ -105,7 +105,7 @@
     sec_count += size;
 
     uint32 send_time = *reinterpret_cast<const uint32*>(data);
-    uint32 recv_time = Time();
+    uint32 recv_time = talk_base::Time();
     uint32 delay = recv_time - send_time;
     sum += delay;
     sum_sq += delay * delay;
diff --git a/base/windowpicker_unittest.cc b/base/windowpicker_unittest.cc
index e1a815d..436854a 100644
--- a/base/windowpicker_unittest.cc
+++ b/base/windowpicker_unittest.cc
@@ -1,4 +1,5 @@
 #include "talk/base/gunit.h"
+#include "talk/base/testutils.h"
 #include "talk/base/window.h"
 #include "talk/base/windowpicker.h"
 #include "talk/base/windowpickerfactory.h"
@@ -10,6 +11,7 @@
 #endif
 
 TEST(WindowPickerTest, GetWindowList) {
+  MAYBE_SKIP_SCREENCAST_TEST();
   if (!talk_base::WindowPickerFactory::IsSupported()) {
     LOG(LS_INFO) << "skipping test: window capturing is not supported with "
                  << "current configuration.";
@@ -24,6 +26,7 @@
 // TODO(hughv) Investigate why this fails on pulse but not locally after
 // upgrading to XCode 4.5.  The failure is GetDesktopList returning FALSE.
 TEST(WindowPickerTest, DISABLE_ON_MAC(GetDesktopList)) {
+  MAYBE_SKIP_SCREENCAST_TEST();
   if (!talk_base::WindowPickerFactory::IsSupported()) {
     LOG(LS_INFO) << "skipping test: window capturing is not supported with "
                  << "current configuration.";
diff --git a/media/base/fakemediaengine.h b/media/base/fakemediaengine.h
index 7ef0c9b..257c198 100644
--- a/media/base/fakemediaengine.h
+++ b/media/base/fakemediaengine.h
@@ -678,18 +678,11 @@
  public:
   FakeBaseEngine()
       : loglevel_(-1),
-        options_(0),
         options_changed_(false),
         fail_create_channel_(false) {}
   bool Init(talk_base::Thread* worker_thread) { return true; }
   void Terminate() {}
 
-  bool SetOptions(int options) {
-    options_ = options;
-    options_changed_ = true;
-    return true;
-  }
-
   void SetLogging(int level, const char* filter) {
     loglevel_ = level;
     logfilter_ = filter;
@@ -704,7 +697,6 @@
  protected:
   int loglevel_;
   std::string logfilter_;
-  int options_;
   // Flag used by optionsmessagehandler_unittest for checking whether any
   // relevant setting has been updated.
   // TODO(thaloun): Replace with explicit checks of before & after values.
@@ -725,6 +717,17 @@
     codecs_.push_back(AudioCodec(101, "fake_audio_codec", 0, 0, 1, 0));
   }
   int GetCapabilities() { return AUDIO_SEND | AUDIO_RECV; }
+  AudioOptions GetAudioOptions() const {
+    return options_;
+  }
+  AudioOptions GetOptions() const {
+    return options_;
+  }
+  bool SetOptions(const AudioOptions& options) {
+    options_ = options;
+    options_changed_ = true;
+    return true;
+  }
 
   VoiceMediaChannel* CreateChannel() {
     if (fail_create_channel_) {
@@ -808,6 +811,7 @@
   std::string out_device_;
   VoiceProcessor* rx_processor_;
   VoiceProcessor* tx_processor_;
+  AudioOptions options_;
 
   friend class FakeMediaEngine;
 };
@@ -819,6 +823,15 @@
     // sanity checks against that.
     codecs_.push_back(VideoCodec(0, "fake_video_codec", 0, 0, 0, 0));
   }
+  bool GetOptions(VideoOptions* options) const {
+    *options = options_;
+    return true;
+  }
+  bool SetOptions(const VideoOptions& options) {
+    options_ = options;
+    options_changed_ = true;
+    return true;
+  }
   int GetCapabilities() { return VIDEO_SEND | VIDEO_RECV; }
   bool SetDefaultEncoderConfig(const VideoEncoderConfig& config) {
     default_encoder_config_ = config;
@@ -883,6 +896,7 @@
   VideoRenderer* renderer_;
   bool capture_;
   VideoProcessor* processor_;
+  VideoOptions options_;
 
   friend class FakeMediaEngine;
 };
@@ -912,7 +926,7 @@
     return video_.GetChannel(index);
   }
 
-  int audio_options() const { return voice_.options_; }
+  AudioOptions audio_options() const { return voice_.options_; }
   int audio_delay_offset() const { return voice_.delay_offset_; }
   int output_volume() const { return voice_.output_volume_; }
   const VideoEncoderConfig& default_video_encoder_config() const {
diff --git a/media/base/filemediaengine.h b/media/base/filemediaengine.h
index 2c8525d..b7a7ac1 100644
--- a/media/base/filemediaengine.h
+++ b/media/base/filemediaengine.h
@@ -86,8 +86,9 @@
   virtual VoiceMediaChannel* CreateChannel();
   virtual VideoMediaChannel* CreateVideoChannel(VoiceMediaChannel* voice_ch);
   virtual SoundclipMedia* CreateSoundclip() { return NULL; }
-  virtual bool SetAudioOptions(int options) { return true; }
-  virtual bool SetVideoOptions(int options) { return true; }
+  virtual AudioOptions GetAudioOptions() const { return AudioOptions(); }
+  virtual bool SetAudioOptions(const AudioOptions& options) { return true; }
+  virtual bool SetVideoOptions(const VideoOptions& options) { return true; }
   virtual bool SetAudioDelayOffset(int offset) { return true; }
   virtual bool SetDefaultVideoEncoderConfig(const VideoEncoderConfig& config) {
     return true;
diff --git a/media/base/filemediaengine_unittest.cc b/media/base/filemediaengine_unittest.cc
index e4d72bb..a72e0dc 100644
--- a/media/base/filemediaengine_unittest.cc
+++ b/media/base/filemediaengine_unittest.cc
@@ -220,8 +220,10 @@
   EXPECT_TRUE(NULL == voice_channel_.get());
   EXPECT_TRUE(NULL == video_channel_.get());
   EXPECT_TRUE(NULL == engine_->CreateSoundclip());
-  EXPECT_TRUE(engine_->SetAudioOptions(0));
-  EXPECT_TRUE(engine_->SetVideoOptions(0));
+  cricket::AudioOptions audio_options;
+  EXPECT_TRUE(engine_->SetAudioOptions(audio_options));
+  cricket::VideoOptions video_options;
+  EXPECT_TRUE(engine_->SetVideoOptions(video_options));
   VideoEncoderConfig video_encoder_config;
   EXPECT_TRUE(engine_->SetDefaultVideoEncoderConfig(video_encoder_config));
   EXPECT_TRUE(engine_->SetSoundDevices(NULL, NULL));
diff --git a/media/base/hybridvideoengine.h b/media/base/hybridvideoengine.h
index ab638c9..7bef97e 100644
--- a/media/base/hybridvideoengine.h
+++ b/media/base/hybridvideoengine.h
@@ -183,8 +183,8 @@
         channel1.release(), channel2.release());
   }
 
-  bool SetOptions(int o) {
-    return video1_.SetOptions(o) && video2_.SetOptions(o);
+  bool SetOptions(const VideoOptions& options) {
+    return video1_.SetOptions(options) && video2_.SetOptions(options);
   }
   bool SetDefaultEncoderConfig(const VideoEncoderConfig& config) {
     VideoEncoderConfig conf = config;
diff --git a/media/base/mediaengine.h b/media/base/mediaengine.h
index 8ebc13b..c9baeb2 100644
--- a/media/base/mediaengine.h
+++ b/media/base/mediaengine.h
@@ -60,30 +60,6 @@
 // proper synchronization between both media types.
 class MediaEngineInterface {
  public:
-  // Bitmask flags for options that may be supported by the media engine
-  // implementation.  This can be converted to and from an
-  // AudioOptions struct for backwards compatibility with calls that
-  // use flags until we transition to using structs everywhere.
-  enum AudioFlags {
-    // Audio processing that attempts to filter away the output signal from
-    // later inbound pickup.
-    ECHO_CANCELLATION         = 1 << 0,
-    // Audio processing to adjust the sensitivity of the local mic dynamically.
-    AUTO_GAIN_CONTROL         = 1 << 1,
-    // Audio processing to filter out background noise.
-    NOISE_SUPPRESSION         = 1 << 2,
-    // Audio processing to remove background noise of lower frequencies.
-    HIGHPASS_FILTER           = 1 << 3,
-    // A switch to swap which captured signal is left and right in stereo mode.
-    STEREO_FLIPPING           = 1 << 4,
-    // Controls delegation echo cancellation to use the OS' facility.
-    SYSTEM_AEC_MODE           = 1 << 5,
-
-    ALL_AUDIO_OPTIONS         = (1 << 6) - 1,
-    DEFAULT_AUDIO_OPTIONS     = ECHO_CANCELLATION | AUTO_GAIN_CONTROL |
-                                NOISE_SUPPRESSION | HIGHPASS_FILTER,
-  };
-
   // Default value to be used for SetAudioDelayOffset().
   static const int kDefaultAudioDelayOffset;
 
@@ -109,10 +85,12 @@
   virtual SoundclipMedia *CreateSoundclip() = 0;
 
   // Configuration
+  // Gets global audio options.
+  virtual AudioOptions GetAudioOptions() const = 0;
   // Sets global audio options. "options" are from AudioOptions, above.
-  virtual bool SetAudioOptions(int options) = 0;
+  virtual bool SetAudioOptions(const AudioOptions& options) = 0;
   // Sets global video options. "options" are from VideoOptions, above.
-  virtual bool SetVideoOptions(int options) = 0;
+  virtual bool SetVideoOptions(const VideoOptions& options) = 0;
   // Sets the value used by the echo canceller to offset delay values obtained
   // from the OS.
   virtual bool SetAudioDelayOffset(int offset) = 0;
@@ -210,11 +188,14 @@
     return voice_.CreateSoundclip();
   }
 
-  virtual bool SetAudioOptions(int o) {
-    return voice_.SetOptions(o);
+  virtual AudioOptions GetAudioOptions() const {
+    return voice_.GetOptions();
   }
-  virtual bool SetVideoOptions(int o) {
-    return video_.SetOptions(o);
+  virtual bool SetAudioOptions(const AudioOptions& options) {
+    return voice_.SetOptions(options);
+  }
+  virtual bool SetVideoOptions(const VideoOptions& options) {
+    return video_.SetOptions(options);
   }
   virtual bool SetAudioDelayOffset(int offset) {
     return voice_.SetDelayOffset(offset);
@@ -304,7 +285,8 @@
     return NULL;
   }
   bool SetDelayOffset(int offset) { return true; }
-  bool SetOptions(int opts) { return true; }
+  AudioOptions GetOptions() const { return AudioOptions(); }
+  bool SetOptions(const AudioOptions& options) { return true; }
   bool SetDevices(const Device* in_device, const Device* out_device) {
     return true;
   }
@@ -344,7 +326,7 @@
       VoiceMediaChannel* voice_media_channel) {
     return NULL;
   }
-  bool SetOptions(int opts) { return true; }
+  bool SetOptions(const VideoOptions& options) { return true; }
   bool SetDefaultEncoderConfig(const VideoEncoderConfig& config) {
     return true;
   }
diff --git a/media/base/rtpdataengine.cc b/media/base/rtpdataengine.cc
index 2a23c10..1254b29 100644
--- a/media/base/rtpdataengine.cc
+++ b/media/base/rtpdataengine.cc
@@ -342,10 +342,6 @@
                     << "; already sent " << send_limiter_->used_in_period()
                     << "/" << send_limiter_->max_per_period();
     return false;
-  } else {
-    LOG(LS_VERBOSE) << "Sending data packet of len=" << packet_len
-                    << "; already sent " << send_limiter_->used_in_period()
-                    << "/" << send_limiter_->max_per_period();
   }
 
   RtpHeader header;
@@ -363,12 +359,12 @@
   packet.AppendData(&kReservedSpace, sizeof(kReservedSpace));
   packet.AppendData(payload.data(), payload.length());
 
-  // Uncomment this for easy debugging.
-  // LOG(LS_INFO) << "Sent packet: "
-  //              << " stream=" << found_stream.id
-  //              << ", seqnum=" << header.seq_num
-  //              << ", timestamp=" << header.timestamp
-  //              << ", len=" << data_len;
+  LOG(LS_VERBOSE) << "Sent RTP data packet: "
+                  << " stream=" << found_stream.id
+                  << " ssrc=" << header.ssrc
+                  << ", seqnum=" << header.seq_num
+                  << ", timestamp=" << header.timestamp
+                  << ", len=" << payload.length();
 
   MediaChannel::SendPacket(&packet);
   send_limiter_->Use(packet_len, now);
diff --git a/media/base/rtpdataengine_unittest.cc b/media/base/rtpdataengine_unittest.cc
index 456f7b0..37ea968 100644
--- a/media/base/rtpdataengine_unittest.cc
+++ b/media/base/rtpdataengine_unittest.cc
@@ -294,7 +294,8 @@
   cricket::RtpHeader header1 = GetSentDataHeader(1);
   EXPECT_EQ(header1.ssrc, 42U);
   EXPECT_EQ(header1.payload_type, 103);
-  EXPECT_EQ(header0.seq_num + 1, header1.seq_num);
+  EXPECT_EQ(static_cast<uint16>(header0.seq_num + 1),
+            static_cast<uint16>(header1.seq_num));
   EXPECT_EQ(header0.timestamp + 180000, header1.timestamp);
 }
 
@@ -354,9 +355,11 @@
   cricket::RtpHeader header1b = GetSentDataHeader(2);
   cricket::RtpHeader header2b = GetSentDataHeader(3);
 
-  EXPECT_EQ(header1a.seq_num + 1, header1b.seq_num);
+  EXPECT_EQ(static_cast<uint16>(header1a.seq_num + 1),
+            static_cast<uint16>(header1b.seq_num));
   EXPECT_EQ(header1a.timestamp + 90000, header1b.timestamp);
-  EXPECT_EQ(header2a.seq_num + 1, header2b.seq_num);
+  EXPECT_EQ(static_cast<uint16>(header2a.seq_num + 1),
+            static_cast<uint16>(header2b.seq_num));
   EXPECT_EQ(header2a.timestamp + 180000, header2b.timestamp);
 }
 
diff --git a/media/base/testutils.h b/media/base/testutils.h
index 4d037b7..136b7b9 100644
--- a/media/base/testutils.h
+++ b/media/base/testutils.h
@@ -28,13 +28,6 @@
 #ifndef TALK_MEDIA_BASE_TESTUTILS_H_
 #define TALK_MEDIA_BASE_TESTUTILS_H_
 
-#ifdef LINUX
-#include <X11/Xlib.h>
-// X defines a few macros that stomp on types that gunit.h uses.
-#undef None
-#undef Bool
-#endif
-
 #include <string>
 #include <vector>
 
@@ -244,38 +237,6 @@
   }
   return false;
 }
-
-#define MAYBE_SKIP_SCREENCAST_TEST() \
-  if (!cricket::IsScreencastingAvailable()) { \
-    LOG(LS_WARNING) << "Skipping test, since it doesn't have the requisite " \
-                    << "X environment for screen capture."; \
-    return; \
-  } \
-
-#ifdef LINUX
-struct XDisplay {
-  XDisplay() : display_(XOpenDisplay(NULL)) { }
-  ~XDisplay() { if (display_) XCloseDisplay(display_); }
-  bool IsValid() const { return display_ != NULL; }
-  operator Display*() { return display_; }
- private:
-  Display* display_;
-};
-#endif
-
-// Returns true if screencasting is available. When false, anything that uses
-// screencasting features may fail.
-inline bool IsScreencastingAvailable() {
-#ifdef LINUX
-  XDisplay display;
-  if (!display.IsValid()) {
-    LOG(LS_WARNING) << "No X Display available.";
-    return false;
-  }
-#endif
-  return true;
-}
-
 }  // namespace cricket
 
 #endif  // TALK_MEDIA_BASE_TESTUTILS_H_
diff --git a/media/webrtc/webrtcvideoengine.cc b/media/webrtc/webrtcvideoengine.cc
index fd7e5bf..cdc1727 100644
--- a/media/webrtc/webrtcvideoengine.cc
+++ b/media/webrtc/webrtcvideoengine.cc
@@ -514,7 +514,8 @@
         external_capture_(external_capture),
         capturer_updated_(false),
         interval_(0),
-        video_adapter_(new CoordinatedVideoAdapter) {
+        video_adapter_(new CoordinatedVideoAdapter),
+        cpu_monitor_(cpu_monitor) {
     overuse_observer_.reset(new WebRtcOveruseObserver(video_adapter_.get()));
     SignalCpuAdaptationUnable.repeat(video_adapter_->SignalCpuAdaptationUnable);
     if (cpu_monitor) {
@@ -633,6 +634,9 @@
   }
 
   void SetCpuOveruseDetection(bool enable) {
+    if (cpu_monitor_ && enable) {
+      cpu_monitor_->SignalUpdate.disconnect(video_adapter_.get());
+    }
     overuse_observer_->Enable(enable);
     video_adapter_->set_cpu_adaptation(enable);
   }
@@ -689,6 +693,7 @@
   int64 interval_;
 
   talk_base::scoped_ptr<CoordinatedVideoAdapter> video_adapter_;
+  talk_base::CpuMonitor* cpu_monitor_;
   talk_base::scoped_ptr<WebRtcOveruseObserver> overuse_observer_;
 };
 
@@ -900,7 +905,7 @@
   return VIDEO_RECV | VIDEO_SEND;
 }
 
-bool WebRtcVideoEngine::SetOptions(int options) {
+bool WebRtcVideoEngine::SetOptions(const VideoOptions &options) {
   return true;
 }
 
@@ -1223,7 +1228,8 @@
 }
 
 // Rebuilds the codec list to be only those that are less intensive
-// than the specified codec. Prefers internal codec over external.
+// than the specified codec. Prefers internal codec over external with
+// higher preference field.
 bool WebRtcVideoEngine::RebuildCodecList(const VideoCodec& in_codec) {
   if (!FindCodec(in_codec))
     return false;
@@ -1262,7 +1268,9 @@
             codecs[i].max_width,
             codecs[i].max_height,
             codecs[i].max_fps,
-            static_cast<int>(codecs.size() + ARRAY_SIZE(kVideoCodecPrefs) - i));
+            // Use negative preference on external codec to ensure the internal
+            // codec is preferred.
+            static_cast<int>(0 - i));
         AddDefaultFeedbackParams(&codec);
         video_codecs_.push_back(codec);
       }
diff --git a/media/webrtc/webrtcvideoengine.h b/media/webrtc/webrtcvideoengine.h
index f0293bb..2489753 100644
--- a/media/webrtc/webrtcvideoengine.h
+++ b/media/webrtc/webrtcvideoengine.h
@@ -104,7 +104,7 @@
   void Terminate();
 
   int GetCapabilities();
-  bool SetOptions(int options);
+  bool SetOptions(const VideoOptions &options);
   bool SetDefaultEncoderConfig(const VideoEncoderConfig& config);
 
   WebRtcVideoMediaChannel* CreateChannel(VoiceMediaChannel* voice_channel);
diff --git a/media/webrtc/webrtcvideoengine_unittest.cc b/media/webrtc/webrtcvideoengine_unittest.cc
index 04662ca..9537673 100644
--- a/media/webrtc/webrtcvideoengine_unittest.cc
+++ b/media/webrtc/webrtcvideoengine_unittest.cc
@@ -1785,8 +1785,12 @@
   encoder_factory_.NotifyCodecsAvailable();
 
   codecs = engine_.codecs();
+  cricket::VideoCodec internal_codec = codecs[0];
+  cricket::VideoCodec external_codec = codecs[codecs.size() - 1];
   // The external codec will appear at last.
-  EXPECT_EQ("GENERIC", codecs[codecs.size() - 1].name);
+  EXPECT_EQ("GENERIC", external_codec.name);
+  // The internal codec is preferred.
+  EXPECT_GE(internal_codec.preference, external_codec.preference);
 }
 
 // Test that external codec with be ignored if it has the same name as one of
diff --git a/media/webrtc/webrtcvoiceengine.cc b/media/webrtc/webrtcvoiceengine.cc
index 2a6ccd7..0e96472 100644
--- a/media/webrtc/webrtcvoiceengine.cc
+++ b/media/webrtc/webrtcvoiceengine.cc
@@ -50,6 +50,7 @@
 #include "talk/media/base/streamparams.h"
 #include "talk/media/base/voiceprocessor.h"
 #include "talk/media/webrtc/webrtcvoe.h"
+#include "webrtc/common.h"
 #include "webrtc/modules/audio_processing/include/audio_processing.h"
 
 #ifdef WIN32
@@ -476,6 +477,24 @@
   return res;
 }
 
+// Gets the default set of optoins applied to the engine. Historically, these
+// were supplied as a combination of flags from the channel manager (ec, agc,
+// ns, and highpass) and the rest hardcoded in InitInternal.
+static AudioOptions GetDefaultEngineOptions() {
+  AudioOptions options;
+  options.echo_cancellation.Set(true);
+  options.auto_gain_control.Set(true);
+  options.noise_suppression.Set(true);
+  options.highpass_filter.Set(true);
+  options.typing_detection.Set(true);
+  options.conference_mode.Set(false);
+  options.adjust_agc_delta.Set(0);
+  options.experimental_agc.Set(false);
+  options.experimental_aec.Set(false);
+  options.aec_dump.Set(false);
+  return options;
+}
+
 bool WebRtcVoiceEngine::InitInternal() {
   // Temporarily turn logging level up for the Init call
   int old_filter = log_filter_;
@@ -506,7 +525,10 @@
     return false;
   }
 
-  if (!SetOptions(MediaEngineInterface::DEFAULT_AUDIO_OPTIONS)) {
+  // Set defaults for options, so that ApplyOptions applies them explicitly
+  // when we clear option (channel) overrides. External clients can still
+  // modify the defaults via SetOptions (on the media engine).
+  if (!SetOptions(GetDefaultEngineOptions())) {
     return false;
   }
 
@@ -594,35 +616,7 @@
   return soundclip;
 }
 
-// TODO(zhurunz): Add a comprehensive unittests for SetOptions().
-bool WebRtcVoiceEngine::SetOptions(int flags) {
-  AudioOptions options;
-
-  // Convert flags to AudioOptions.
-  options.echo_cancellation.Set(
-      ((flags & MediaEngineInterface::ECHO_CANCELLATION) != 0));
-  options.auto_gain_control.Set(
-      ((flags & MediaEngineInterface::AUTO_GAIN_CONTROL) != 0));
-  options.noise_suppression.Set(
-      ((flags & MediaEngineInterface::NOISE_SUPPRESSION) != 0));
-  options.highpass_filter.Set(
-      ((flags & MediaEngineInterface::HIGHPASS_FILTER) != 0));
-  options.stereo_swapping.Set(
-      ((flags & MediaEngineInterface::STEREO_FLIPPING) != 0));
-
-  // Set defaults for flagless options here. Make sure they are all set so that
-  // ApplyOptions applies all of them when we clear overrides.
-  options.typing_detection.Set(true);
-  options.conference_mode.Set(false);
-  options.adjust_agc_delta.Set(0);
-  options.experimental_agc.Set(false);
-  options.experimental_aec.Set(false);
-  options.aec_dump.Set(false);
-
-  return SetAudioOptions(options);
-}
-
-bool WebRtcVoiceEngine::SetAudioOptions(const AudioOptions& options) {
+bool WebRtcVoiceEngine::SetOptions(const AudioOptions& options) {
   if (!ApplyOptions(options)) {
     return false;
   }
@@ -684,7 +678,6 @@
   options.experimental_aec.Set(false);
 #endif
 
-
   LOG(LS_INFO) << "Applying audio options: " << options.ToString();
 
   webrtc::VoEAudioProcessing* voep = voe_wrapper_->processing();
@@ -766,6 +759,20 @@
       StopAecDump();
   }
 
+  bool experimental_aec;
+  if (options.experimental_aec.Get(&experimental_aec)) {
+    webrtc::AudioProcessing* audioproc =
+        voe_wrapper_->base()->audio_processing();
+    // We check audioproc for the benefit of tests, since FakeWebRtcVoiceEngine
+    // returns NULL on audio_processing().
+    if (audioproc) {
+      webrtc::Config config;
+      config.Set<webrtc::DelayCorrection>(
+          new webrtc::DelayCorrection(experimental_aec));
+      audioproc->SetExtraOptions(config);
+    }
+  }
+
 
   return true;
 }
@@ -2751,7 +2758,6 @@
     }
   }
 
-
   webrtc::CallStatistics cs;
   unsigned int ssrc;
   webrtc::CodecInst codec;
@@ -2819,6 +2825,8 @@
     sinfo.echo_return_loss_enhancement = echo_return_loss_enhancement;
     sinfo.echo_delay_median_ms = echo_delay_median_ms;
     sinfo.echo_delay_std_ms = echo_delay_std_ms;
+    // TODO(ajm): Re-enable this metric once we have a reliable implementation.
+    sinfo.aec_quality_min = -1;
     sinfo.typing_noise_detected = typing_noise_detected_;
 
     info->senders.push_back(sinfo);
@@ -2928,13 +2936,11 @@
 }
 
 void WebRtcVoiceMediaChannel::OnError(uint32 ssrc, int error) {
-#ifdef USE_WEBRTC_DEV_BRANCH
   if (error == VE_TYPING_NOISE_WARNING) {
     typing_noise_detected_ = true;
   } else if (error == VE_TYPING_NOISE_OFF_WARNING) {
     typing_noise_detected_ = false;
   }
-#endif
   SignalMediaError(ssrc, WebRtcErrorToChannelError(error));
 }
 
diff --git a/media/webrtc/webrtcvoiceengine.h b/media/webrtc/webrtcvoiceengine.h
index 6cb0b30..6e11485 100644
--- a/media/webrtc/webrtcvoiceengine.h
+++ b/media/webrtc/webrtcvoiceengine.h
@@ -105,12 +105,8 @@
 
   SoundclipMedia* CreateSoundclip();
 
-  // TODO(pthatcher): Rename to SetOptions and replace the old
-  // flags-based SetOptions.
-  bool SetAudioOptions(const AudioOptions& options);
-  // Eventually, we will replace them with AudioOptions.
-  // In the meantime, we leave this here for backwards compat.
-  bool SetOptions(int flags);
+  AudioOptions GetOptions() const { return options_; }
+  bool SetOptions(const AudioOptions& options);
   // Overrides, when set, take precedence over the options on a
   // per-option basis.  For example, if AGC is set in options and AEC
   // is set in overrides, AGC and AEC will be both be set.  Overrides
diff --git a/media/webrtc/webrtcvoiceengine_unittest.cc b/media/webrtc/webrtcvoiceengine_unittest.cc
index 3710e7e..2b2d36a 100644
--- a/media/webrtc/webrtcvoiceengine_unittest.cc
+++ b/media/webrtc/webrtcvoiceengine_unittest.cc
@@ -2321,7 +2321,7 @@
 
   // Nothing set, so all ignored.
   cricket::AudioOptions options;
-  ASSERT_TRUE(engine_.SetAudioOptions(options));
+  ASSERT_TRUE(engine_.SetOptions(options));
   voe_.GetEcStatus(ec_enabled, ec_mode);
   voe_.GetEcMetricsStatus(ec_metrics_enabled);
   voe_.GetAecmMode(aecm_mode, cng_enabled);
@@ -2345,14 +2345,14 @@
 
   // Turn echo cancellation off
   options.echo_cancellation.Set(false);
-  ASSERT_TRUE(engine_.SetAudioOptions(options));
+  ASSERT_TRUE(engine_.SetOptions(options));
   voe_.GetEcStatus(ec_enabled, ec_mode);
   EXPECT_FALSE(ec_enabled);
 
   // Turn echo cancellation back on, with settings, and make sure
   // nothing else changed.
   options.echo_cancellation.Set(true);
-  ASSERT_TRUE(engine_.SetAudioOptions(options));
+  ASSERT_TRUE(engine_.SetOptions(options));
   voe_.GetEcStatus(ec_enabled, ec_mode);
   voe_.GetEcMetricsStatus(ec_metrics_enabled);
   voe_.GetAecmMode(aecm_mode, cng_enabled);
@@ -2375,14 +2375,14 @@
 
   // Turn off AGC
   options.auto_gain_control.Set(false);
-  ASSERT_TRUE(engine_.SetAudioOptions(options));
+  ASSERT_TRUE(engine_.SetOptions(options));
   voe_.GetAgcStatus(agc_enabled, agc_mode);
   EXPECT_FALSE(agc_enabled);
 
   // Turn AGC back on
   options.auto_gain_control.Set(true);
   options.adjust_agc_delta.Clear();
-  ASSERT_TRUE(engine_.SetAudioOptions(options));
+  ASSERT_TRUE(engine_.SetOptions(options));
   voe_.GetAgcStatus(agc_enabled, agc_mode);
   EXPECT_TRUE(agc_enabled);
   voe_.GetAgcConfig(agc_config);
@@ -2393,7 +2393,7 @@
   options.highpass_filter.Set(false);
   options.typing_detection.Set(false);
   options.stereo_swapping.Set(true);
-  ASSERT_TRUE(engine_.SetAudioOptions(options));
+  ASSERT_TRUE(engine_.SetOptions(options));
   voe_.GetNsStatus(ns_enabled, ns_mode);
   highpass_filter_enabled = voe_.IsHighPassFilterEnabled();
   stereo_swapping_enabled = voe_.IsStereoChannelSwappingEnabled();
@@ -2405,7 +2405,7 @@
 
   // Turn on "conference mode" to ensure it has no impact.
   options.conference_mode.Set(true);
-  ASSERT_TRUE(engine_.SetAudioOptions(options));
+  ASSERT_TRUE(engine_.SetOptions(options));
   voe_.GetEcStatus(ec_enabled, ec_mode);
   voe_.GetNsStatus(ns_enabled, ns_mode);
   EXPECT_TRUE(ec_enabled);
@@ -2414,7 +2414,7 @@
   EXPECT_EQ(webrtc::kNsHighSuppression, ns_mode);
 }
 
-TEST_F(WebRtcVoiceEngineTestFake, SetOptions) {
+TEST_F(WebRtcVoiceEngineTestFake, DefaultOptions) {
   EXPECT_TRUE(SetupEngine());
 
   bool ec_enabled;
@@ -2428,103 +2428,6 @@
   bool stereo_swapping_enabled;
   bool typing_detection_enabled;
 
-  ASSERT_TRUE(engine_.SetOptions(0));
-  voe_.GetEcStatus(ec_enabled, ec_mode);
-  voe_.GetEcMetricsStatus(ec_metrics_enabled);
-  voe_.GetAgcStatus(agc_enabled, agc_mode);
-  voe_.GetNsStatus(ns_enabled, ns_mode);
-  highpass_filter_enabled = voe_.IsHighPassFilterEnabled();
-  stereo_swapping_enabled = voe_.IsStereoChannelSwappingEnabled();
-  voe_.GetTypingDetectionStatus(typing_detection_enabled);
-  EXPECT_FALSE(ec_enabled);
-  EXPECT_FALSE(agc_enabled);
-  EXPECT_FALSE(ns_enabled);
-  EXPECT_FALSE(highpass_filter_enabled);
-  EXPECT_FALSE(stereo_swapping_enabled);
-  EXPECT_TRUE(typing_detection_enabled);
-
-  ASSERT_TRUE(engine_.SetOptions(
-      cricket::MediaEngineInterface::ECHO_CANCELLATION));
-  voe_.GetEcStatus(ec_enabled, ec_mode);
-  voe_.GetEcMetricsStatus(ec_metrics_enabled);
-  voe_.GetAgcStatus(agc_enabled, agc_mode);
-  voe_.GetNsStatus(ns_enabled, ns_mode);
-  highpass_filter_enabled = voe_.IsHighPassFilterEnabled();
-  stereo_swapping_enabled = voe_.IsStereoChannelSwappingEnabled();
-  voe_.GetTypingDetectionStatus(typing_detection_enabled);
-  EXPECT_TRUE(ec_enabled);
-  EXPECT_FALSE(agc_enabled);
-  EXPECT_FALSE(ns_enabled);
-  EXPECT_FALSE(highpass_filter_enabled);
-  EXPECT_FALSE(stereo_swapping_enabled);
-  EXPECT_TRUE(typing_detection_enabled);
-
-  ASSERT_TRUE(engine_.SetOptions(
-      cricket::MediaEngineInterface::AUTO_GAIN_CONTROL));
-  voe_.GetEcStatus(ec_enabled, ec_mode);
-  voe_.GetEcMetricsStatus(ec_metrics_enabled);
-  voe_.GetAgcStatus(agc_enabled, agc_mode);
-  voe_.GetNsStatus(ns_enabled, ns_mode);
-  highpass_filter_enabled = voe_.IsHighPassFilterEnabled();
-  stereo_swapping_enabled = voe_.IsStereoChannelSwappingEnabled();
-  voe_.GetTypingDetectionStatus(typing_detection_enabled);
-  EXPECT_FALSE(ec_enabled);
-  EXPECT_TRUE(agc_enabled);
-  EXPECT_FALSE(ns_enabled);
-  EXPECT_FALSE(highpass_filter_enabled);
-  EXPECT_FALSE(stereo_swapping_enabled);
-  EXPECT_TRUE(typing_detection_enabled);
-
-  ASSERT_TRUE(engine_.SetOptions(
-      cricket::MediaEngineInterface::NOISE_SUPPRESSION));
-  voe_.GetEcStatus(ec_enabled, ec_mode);
-  voe_.GetEcMetricsStatus(ec_metrics_enabled);
-  voe_.GetAgcStatus(agc_enabled, agc_mode);
-  voe_.GetNsStatus(ns_enabled, ns_mode);
-  highpass_filter_enabled = voe_.IsHighPassFilterEnabled();
-  stereo_swapping_enabled = voe_.IsStereoChannelSwappingEnabled();
-  voe_.GetTypingDetectionStatus(typing_detection_enabled);
-  EXPECT_FALSE(ec_enabled);
-  EXPECT_FALSE(agc_enabled);
-  EXPECT_TRUE(ns_enabled);
-  EXPECT_FALSE(highpass_filter_enabled);
-  EXPECT_FALSE(stereo_swapping_enabled);
-  EXPECT_TRUE(typing_detection_enabled);
-
-  ASSERT_TRUE(engine_.SetOptions(
-      cricket::MediaEngineInterface::HIGHPASS_FILTER));
-  voe_.GetEcStatus(ec_enabled, ec_mode);
-  voe_.GetEcMetricsStatus(ec_metrics_enabled);
-  voe_.GetAgcStatus(agc_enabled, agc_mode);
-  voe_.GetNsStatus(ns_enabled, ns_mode);
-  highpass_filter_enabled = voe_.IsHighPassFilterEnabled();
-  stereo_swapping_enabled = voe_.IsStereoChannelSwappingEnabled();
-  voe_.GetTypingDetectionStatus(typing_detection_enabled);
-  EXPECT_FALSE(ec_enabled);
-  EXPECT_FALSE(agc_enabled);
-  EXPECT_FALSE(ns_enabled);
-  EXPECT_TRUE(highpass_filter_enabled);
-  EXPECT_FALSE(stereo_swapping_enabled);
-  EXPECT_TRUE(typing_detection_enabled);
-
-  ASSERT_TRUE(engine_.SetOptions(
-      cricket::MediaEngineInterface::STEREO_FLIPPING));
-  voe_.GetEcStatus(ec_enabled, ec_mode);
-  voe_.GetEcMetricsStatus(ec_metrics_enabled);
-  voe_.GetAgcStatus(agc_enabled, agc_mode);
-  voe_.GetNsStatus(ns_enabled, ns_mode);
-  highpass_filter_enabled = voe_.IsHighPassFilterEnabled();
-  stereo_swapping_enabled = voe_.IsStereoChannelSwappingEnabled();
-  voe_.GetTypingDetectionStatus(typing_detection_enabled);
-  EXPECT_FALSE(ec_enabled);
-  EXPECT_FALSE(agc_enabled);
-  EXPECT_FALSE(ns_enabled);
-  EXPECT_FALSE(highpass_filter_enabled);
-  EXPECT_TRUE(stereo_swapping_enabled);
-  EXPECT_TRUE(typing_detection_enabled);
-
-  ASSERT_TRUE(engine_.SetOptions(
-      cricket::MediaEngineInterface::DEFAULT_AUDIO_OPTIONS));
   voe_.GetEcStatus(ec_enabled, ec_mode);
   voe_.GetEcMetricsStatus(ec_metrics_enabled);
   voe_.GetAgcStatus(agc_enabled, agc_mode);
@@ -2536,24 +2439,8 @@
   EXPECT_TRUE(agc_enabled);
   EXPECT_TRUE(ns_enabled);
   EXPECT_TRUE(highpass_filter_enabled);
+  EXPECT_TRUE(typing_detection_enabled);
   EXPECT_FALSE(stereo_swapping_enabled);
-  EXPECT_TRUE(typing_detection_enabled);
-
-  ASSERT_TRUE(engine_.SetOptions(
-      cricket::MediaEngineInterface::ALL_AUDIO_OPTIONS));
-  voe_.GetEcStatus(ec_enabled, ec_mode);
-  voe_.GetEcMetricsStatus(ec_metrics_enabled);
-  voe_.GetAgcStatus(agc_enabled, agc_mode);
-  voe_.GetNsStatus(ns_enabled, ns_mode);
-  highpass_filter_enabled = voe_.IsHighPassFilterEnabled();
-  stereo_swapping_enabled = voe_.IsStereoChannelSwappingEnabled();
-  voe_.GetTypingDetectionStatus(typing_detection_enabled);
-  EXPECT_TRUE(ec_enabled);
-  EXPECT_TRUE(agc_enabled);
-  EXPECT_TRUE(ns_enabled);
-  EXPECT_TRUE(highpass_filter_enabled);
-  EXPECT_TRUE(stereo_swapping_enabled);
-  EXPECT_TRUE(typing_detection_enabled);
 }
 
 TEST_F(WebRtcVoiceEngineTestFake, InitDoesNotOverwriteDefaultAgcConfig) {
@@ -2625,7 +2512,7 @@
   ASSERT_TRUE(channel2->GetOptions(&actual_options));
   EXPECT_EQ(expected_options, actual_options);
 
-  ASSERT_TRUE(engine_.SetAudioOptions(options_all));
+  ASSERT_TRUE(engine_.SetOptions(options_all));
   bool ec_enabled;
   webrtc::EcModes ec_mode;
   bool agc_enabled;
@@ -2672,7 +2559,7 @@
   EXPECT_TRUE(ns_enabled);
 
   // Make sure settings take effect while we are sending.
-  ASSERT_TRUE(engine_.SetAudioOptions(options_all));
+  ASSERT_TRUE(engine_.SetOptions(options_all));
   cricket::AudioOptions options_no_agc_nor_ns;
   options_no_agc_nor_ns.auto_gain_control.Set(false);
   options_no_agc_nor_ns.noise_suppression.Set(false);
diff --git a/p2p/base/fakesession.h b/p2p/base/fakesession.h
index d162950..bc05ce2 100644
--- a/p2p/base/fakesession.h
+++ b/p2p/base/fakesession.h
@@ -391,6 +391,12 @@
                     NULL, "", "", initiator),
       fail_create_channel_(false) {
   }
+  FakeSession(bool initiator, talk_base::Thread* worker_thread)
+      : BaseSession(talk_base::Thread::Current(),
+                    worker_thread,
+                    NULL, "", "", initiator),
+      fail_create_channel_(false) {
+  }
 
   FakeTransport* GetTransport(const std::string& content_name) {
     return static_cast<FakeTransport*>(
diff --git a/p2p/base/p2ptransportchannel.cc b/p2p/base/p2ptransportchannel.cc
index d45a66c..8bdaa9a 100644
--- a/p2p/base/p2ptransportchannel.cc
+++ b/p2p/base/p2ptransportchannel.cc
@@ -518,19 +518,21 @@
     // request came from.
 
     // There shouldn't be an existing connection with this remote address.
-    // When ports are muxed, this channel might get multiple unknown addres
+    // When ports are muxed, this channel might get multiple unknown address
     // signals. In that case if the connection is already exists, we should
     // simply ignore the signal othewise send server error.
-    if (port->GetConnection(new_remote_candidate.address()) && port_muxed) {
-      LOG(LS_INFO) << "Connection already exist for PeerReflexive candidate: "
-                   << new_remote_candidate.ToString();
-      return;
-    } else if (port->GetConnection(new_remote_candidate.address())) {
-      ASSERT(false);
-      port->SendBindingErrorResponse(stun_msg, address,
-                                     STUN_ERROR_SERVER_ERROR,
-                                     STUN_ERROR_REASON_SERVER_ERROR);
-      return;
+    if (port->GetConnection(new_remote_candidate.address())) {
+      if (port_muxed) {
+        LOG(LS_INFO) << "Connection already exists for peer reflexive "
+                     << "candidate: " << new_remote_candidate.ToString();
+        return;
+      } else {
+        ASSERT(false);
+        port->SendBindingErrorResponse(stun_msg, address,
+                                       STUN_ERROR_SERVER_ERROR,
+                                       STUN_ERROR_REASON_SERVER_ERROR);
+        return;
+      }
     }
 
     Connection* connection = port->CreateConnection(
diff --git a/p2p/base/session.cc b/p2p/base/session.cc
index 3128393..e5c86c1 100644
--- a/p2p/base/session.cc
+++ b/p2p/base/session.cc
@@ -26,6 +26,8 @@
  */
 
 #include "talk/p2p/base/session.h"
+
+#include "talk/base/bind.h"
 #include "talk/base/common.h"
 #include "talk/base/logging.h"
 #include "talk/base/helpers.h"
@@ -44,6 +46,8 @@
 
 namespace cricket {
 
+using talk_base::Bind;
+
 bool BadMessage(const buzz::QName type,
                 const std::string& text,
                 MessageError* err) {
@@ -65,11 +69,13 @@
 }
 
 TransportChannel* TransportProxy::GetChannel(int component) {
+  ASSERT(talk_base::Thread::Current() == worker_thread_);
   return GetChannelProxy(component);
 }
 
 TransportChannel* TransportProxy::CreateChannel(
     const std::string& name, int component) {
+  ASSERT(talk_base::Thread::Current() == worker_thread_);
   ASSERT(GetChannel(component) == NULL);
   ASSERT(!transport_->get()->HasChannel(component));
 
@@ -81,9 +87,9 @@
   // If we're already negotiated, create an impl and hook it up to the proxy
   // channel. If we're connecting, create an impl but don't hook it up yet.
   if (negotiated_) {
-    SetChannelProxyImpl(component, channel);
+    SetupChannelProxy_w(component, channel);
   } else if (connecting_) {
-    GetOrCreateChannelProxyImpl(component);
+    GetOrCreateChannelProxyImpl_w(component);
   }
   return channel;
 }
@@ -93,6 +99,7 @@
 }
 
 void TransportProxy::DestroyChannel(int component) {
+  ASSERT(talk_base::Thread::Current() == worker_thread_);
   TransportChannel* channel = GetChannel(component);
   if (channel) {
     // If the state of TransportProxy is not NEGOTIATED
@@ -100,7 +107,7 @@
     // connected. Both must be connected before
     // deletion.
     if (!negotiated_) {
-      SetChannelProxyImpl(component, GetChannelProxy(component));
+      SetupChannelProxy_w(component, GetChannelProxy(component));
     }
 
     channels_.erase(component);
@@ -130,7 +137,7 @@
   if (!negotiated_) {
     for (ChannelMap::iterator iter = channels_.begin();
          iter != channels_.end(); ++iter) {
-      SetChannelProxyImpl(iter->first, iter->second);
+      SetupChannelProxy(iter->first, iter->second);
     }
     negotiated_ = true;
   }
@@ -191,6 +198,13 @@
 
 TransportChannelImpl* TransportProxy::GetOrCreateChannelProxyImpl(
     int component) {
+  return worker_thread_->Invoke<TransportChannelImpl*>(Bind(
+      &TransportProxy::GetOrCreateChannelProxyImpl_w, this, component));
+}
+
+TransportChannelImpl* TransportProxy::GetOrCreateChannelProxyImpl_w(
+    int component) {
+  ASSERT(talk_base::Thread::Current() == worker_thread_);
   TransportChannelImpl* impl = transport_->get()->GetChannel(component);
   if (impl == NULL) {
     impl = transport_->get()->CreateChannel(component);
@@ -198,13 +212,33 @@
   return impl;
 }
 
-void TransportProxy::SetChannelProxyImpl(
+void TransportProxy::SetupChannelProxy(
     int component, TransportChannelProxy* transproxy) {
+  worker_thread_->Invoke<void>(Bind(
+      &TransportProxy::SetupChannelProxy_w, this, component, transproxy));
+}
+
+void TransportProxy::SetupChannelProxy_w(
+    int component, TransportChannelProxy* transproxy) {
+  ASSERT(talk_base::Thread::Current() == worker_thread_);
   TransportChannelImpl* impl = GetOrCreateChannelProxyImpl(component);
   ASSERT(impl != NULL);
   transproxy->SetImplementation(impl);
 }
 
+void TransportProxy::ReplaceChannelProxyImpl(TransportChannelProxy* proxy,
+                                             TransportChannelImpl* impl) {
+  worker_thread_->Invoke<void>(Bind(
+      &TransportProxy::ReplaceChannelProxyImpl_w, this, proxy, impl));
+}
+
+void TransportProxy::ReplaceChannelProxyImpl_w(TransportChannelProxy* proxy,
+                                               TransportChannelImpl* impl) {
+  ASSERT(talk_base::Thread::Current() == worker_thread_);
+  ASSERT(proxy != NULL);
+  proxy->SetImplementation(impl);
+}
+
 // This function muxes |this| onto |target| by repointing |this| at
 // |target|'s transport and setting our TransportChannelProxies
 // to point to |target|'s underlying implementations.
@@ -220,12 +254,12 @@
        iter != channels_.end(); ++iter) {
     if (!target->transport_->get()->HasChannel(iter->first)) {
       // Remove if channel doesn't exist in |transport_|.
-      iter->second->SetImplementation(NULL);
+      ReplaceChannelProxyImpl(iter->second, NULL);
     } else {
       // Replace the impl for all the TransportProxyChannels with the channels
       // from |target|'s transport. Fail if there's not an exact match.
-      iter->second->SetImplementation(
-          target->transport_->get()->CreateChannel(iter->first));
+      ReplaceChannelProxyImpl(
+          iter->second, target->transport_->get()->CreateChannel(iter->first));
     }
   }
 
@@ -489,7 +523,7 @@
   transport->SignalRoleConflict.connect(
       this, &BaseSession::OnRoleConflict);
 
-  transproxy = new TransportProxy(sid_, content_name,
+  transproxy = new TransportProxy(worker_thread_, sid_, content_name,
                                   new TransportWrapper(transport));
   transproxy->SignalCandidatesReady.connect(
       this, &BaseSession::OnTransportProxyCandidatesReady);
diff --git a/p2p/base/session.h b/p2p/base/session.h
index e05e3c0..12310bc 100644
--- a/p2p/base/session.h
+++ b/p2p/base/session.h
@@ -91,10 +91,12 @@
                        public CandidateTranslator {
  public:
   TransportProxy(
+      talk_base::Thread* worker_thread,
       const std::string& sid,
       const std::string& content_name,
       TransportWrapper* transport)
-      : sid_(sid),
+      : worker_thread_(worker_thread),
+        sid_(sid),
         content_name_(content_name),
         transport_(transport),
         connecting_(false),
@@ -168,12 +170,21 @@
  private:
   TransportChannelProxy* GetChannelProxy(int component) const;
   TransportChannelProxy* GetChannelProxyByName(const std::string& name) const;
-  void ReplaceChannelProxyImpl(TransportChannelProxy* channel_proxy,
-                               size_t index);
-  TransportChannelImpl* GetOrCreateChannelProxyImpl(int component);
-  void SetChannelProxyImpl(int component,
-                           TransportChannelProxy* proxy);
 
+  TransportChannelImpl* GetOrCreateChannelProxyImpl(int component);
+  TransportChannelImpl* GetOrCreateChannelProxyImpl_w(int component);
+
+  // Manipulators of transportchannelimpl in channel proxy.
+  void SetupChannelProxy(int component,
+                           TransportChannelProxy* proxy);
+  void SetupChannelProxy_w(int component,
+                             TransportChannelProxy* proxy);
+  void ReplaceChannelProxyImpl(TransportChannelProxy* proxy,
+                               TransportChannelImpl* impl);
+  void ReplaceChannelProxyImpl_w(TransportChannelProxy* proxy,
+                                 TransportChannelImpl* impl);
+
+  talk_base::Thread* worker_thread_;
   std::string sid_;
   std::string content_name_;
   talk_base::scoped_refptr<TransportWrapper> transport_;
diff --git a/p2p/base/transportchannelproxy.cc b/p2p/base/transportchannelproxy.cc
index 04b32ce..318d133 100644
--- a/p2p/base/transportchannelproxy.cc
+++ b/p2p/base/transportchannelproxy.cc
@@ -27,6 +27,7 @@
 
 #include "talk/p2p/base/transportchannelproxy.h"
 #include "talk/base/common.h"
+#include "talk/base/logging.h"
 #include "talk/base/thread.h"
 #include "talk/p2p/base/transport.h"
 #include "talk/p2p/base/transportchannelimpl.h"
@@ -54,8 +55,15 @@
 }
 
 void TransportChannelProxy::SetImplementation(TransportChannelImpl* impl) {
-  // TODO(juberti): Fix this to occur on the correct thread.
-  // ASSERT(talk_base::Thread::Current() == worker_thread_);
+  ASSERT(talk_base::Thread::Current() == worker_thread_);
+
+  if (impl == impl_) {
+    ASSERT(false);
+    // Ignore if the |impl| has already been set.
+    LOG(LS_WARNING) << "Ignored TransportChannelProxy::SetImplementation call "
+                    << "with a same impl as the existing one.";
+    return;
+  }
 
   // Destroy any existing impl_.
   if (impl_) {
diff --git a/session/media/call.cc b/session/media/call.cc
index c963c36..51a721b 100644
--- a/session/media/call.cc
+++ b/session/media/call.cc
@@ -1079,7 +1079,11 @@
   const SessionDescription* offer = session_client_->CreateOffer(options);
 
   Session* session = session_client_->CreateSession(id, this);
-  session->set_initiator_name(initiator_name);
+  // Only override the initiator_name if it was manually supplied. Otherwise,
+  // session_client_ will supply the local jid as initiator in CreateOffer.
+  if (!initiator_name.empty()) {
+    session->set_initiator_name(initiator_name);
+  }
 
   AddSession(session, offer);
   session->Initiate(to.Str(), offer);
diff --git a/session/media/call.h b/session/media/call.h
index 9b0a6c9..efb4ea3 100644
--- a/session/media/call.h
+++ b/session/media/call.h
@@ -117,6 +117,9 @@
     MediaStreams* recv_streams = GetMediaStreams(session);
     return recv_streams ? &recv_streams->audio() : NULL;
   }
+  VoiceChannel* GetVoiceChannel(Session* session) const;
+  VideoChannel* GetVideoChannel(Session* session) const;
+  DataChannel* GetDataChannel(Session* session) const;
   // Public just for unit tests
   VideoContentDescription* CreateVideoStreamUpdate(const StreamParams& stream);
   // Takes ownership of video.
@@ -193,9 +196,6 @@
   void OnDataReceived(DataChannel* channel,
                       const ReceiveDataParams& params,
                       const talk_base::Buffer& payload);
-  VoiceChannel* GetVoiceChannel(Session* session) const;
-  VideoChannel* GetVideoChannel(Session* session) const;
-  DataChannel* GetDataChannel(Session* session) const;
   MediaStreams* GetMediaStreams(Session* session) const;
   void UpdateRemoteMediaStreams(Session* session,
                                 const ContentInfos& updated_contents,
diff --git a/session/media/channel.cc b/session/media/channel.cc
index f6259e9..cfe58a7 100644
--- a/session/media/channel.cc
+++ b/session/media/channel.cc
@@ -641,12 +641,6 @@
 
 bool BaseChannel::SendPacket(bool rtcp, talk_base::Buffer* packet,
                              talk_base::DiffServCodePoint dscp) {
-  // Unless we're sending optimistically, we only allow packets through when we
-  // are completely writable.
-  if (!optimistic_data_send_ && !writable_) {
-    return false;
-  }
-
   // SendPacket gets called from MediaEngine, typically on an encoder thread.
   // If the thread is not our worker thread, we will post to our worker
   // so that the real work happens on our worker. This avoids us having to
diff --git a/session/media/channelmanager.cc b/session/media/channelmanager.cc
index 36c7183..47f0fc5 100644
--- a/session/media/channelmanager.cc
+++ b/session/media/channelmanager.cc
@@ -116,9 +116,10 @@
   initialized_ = false;
   main_thread_ = talk_base::Thread::Current();
   worker_thread_ = worker_thread;
+  // Get the default audio options from the media engine.
+  audio_options_ = media_engine_->GetAudioOptions();
   audio_in_device_ = DeviceManagerInterface::kDefaultDeviceName;
   audio_out_device_ = DeviceManagerInterface::kDefaultDeviceName;
-  audio_options_ = MediaEngineInterface::DEFAULT_AUDIO_OPTIONS;
   audio_delay_offset_ = MediaEngineInterface::kDefaultAudioDelayOffset;
   audio_output_volume_ = kNotSetOutputVolume;
   local_renderer_ = NULL;
@@ -251,7 +252,7 @@
         LOG(LS_WARNING) << "Failed to SetAudioOptions with"
                         << " microphone: " << audio_in_device_
                         << " speaker: " << audio_out_device_
-                        << " options: " << audio_options_
+                        << " options: " << audio_options_.ToString()
                         << " delay: " << audio_delay_offset_;
       }
 
@@ -502,23 +503,26 @@
 }
 
 bool ChannelManager::GetAudioOptions(std::string* in_name,
-                                     std::string* out_name, int* opts) {
+                                     std::string* out_name,
+                                     AudioOptions* options) {
   if (in_name)
     *in_name = audio_in_device_;
   if (out_name)
     *out_name = audio_out_device_;
-  if (opts)
-    *opts = audio_options_;
+  if (options)
+    *options = audio_options_;
   return true;
 }
 
 bool ChannelManager::SetAudioOptions(const std::string& in_name,
-                                     const std::string& out_name, int opts) {
-  return SetAudioOptions(in_name, out_name, opts, audio_delay_offset_);
+                                     const std::string& out_name,
+                                     const AudioOptions& options) {
+  return SetAudioOptions(in_name, out_name, options, audio_delay_offset_);
 }
 
 bool ChannelManager::SetAudioOptions(const std::string& in_name,
-                                     const std::string& out_name, int opts,
+                                     const std::string& out_name,
+                                     const AudioOptions& options,
                                      int delay_offset) {
   // Get device ids from DeviceManager.
   Device in_dev, out_dev;
@@ -536,12 +540,12 @@
   if (initialized_) {
     ret = worker_thread_->Invoke<bool>(
         Bind(&ChannelManager::SetAudioOptions_w, this,
-             opts, delay_offset, &in_dev, &out_dev));
+             options, delay_offset, &in_dev, &out_dev));
   }
 
   // If all worked well, save the values for use in GetAudioOptions.
   if (ret) {
-    audio_options_ = opts;
+    audio_options_ = options;
     audio_in_device_ = in_name;
     audio_out_device_ = out_name;
     audio_delay_offset_ = delay_offset;
@@ -549,13 +553,14 @@
   return ret;
 }
 
-bool ChannelManager::SetAudioOptions_w(int opts, int delay_offset,
+bool ChannelManager::SetAudioOptions_w(
+    const AudioOptions& options, int delay_offset,
     const Device* in_dev, const Device* out_dev) {
   ASSERT(worker_thread_ == talk_base::Thread::Current());
   ASSERT(initialized_);
 
   // Set audio options
-  bool ret = media_engine_->SetAudioOptions(opts);
+  bool ret = media_engine_->SetAudioOptions(options);
 
   if (ret) {
     ret = media_engine_->SetAudioDelayOffset(delay_offset);
diff --git a/session/media/channelmanager.h b/session/media/channelmanager.h
index 04af5e1..af95f92 100644
--- a/session/media/channelmanager.h
+++ b/session/media/channelmanager.h
@@ -137,9 +137,11 @@
   // Configures the audio and video devices. A null pointer can be passed to
   // GetAudioOptions() for any parameter of no interest.
   bool GetAudioOptions(std::string* wave_in_device,
-                       std::string* wave_out_device, int* opts);
+                       std::string* wave_out_device,
+                       AudioOptions* options);
   bool SetAudioOptions(const std::string& wave_in_device,
-                       const std::string& wave_out_device, int opts);
+                       const std::string& wave_out_device,
+                       const AudioOptions& options);
   bool GetOutputVolume(int* level);
   bool SetOutputVolume(int level);
   bool IsSameCapturer(const std::string& capturer_name,
@@ -227,7 +229,8 @@
   // Adds non-transient parameters which can only be changed through the
   // options store.
   bool SetAudioOptions(const std::string& wave_in_device,
-                       const std::string& wave_out_device, int opts,
+                       const std::string& wave_out_device,
+                       const AudioOptions& options,
                        int delay_offset);
   int audio_delay_offset() const { return audio_delay_offset_; }
 
@@ -256,8 +259,8 @@
   void DestroyDataChannel_w(DataChannel* data_channel);
   Soundclip* CreateSoundclip_w();
   void DestroySoundclip_w(Soundclip* soundclip);
-  bool SetAudioOptions_w(int opts, int delay_offset, const Device* in_dev,
-                         const Device* out_dev);
+  bool SetAudioOptions_w(const AudioOptions& options, int delay_offset,
+                         const Device* in_dev, const Device* out_dev);
   bool SetCaptureDevice_w(const Device* cam_device);
   void OnVideoCaptureStateChange(VideoCapturer* capturer,
                                  CaptureState result);
@@ -283,7 +286,7 @@
 
   std::string audio_in_device_;
   std::string audio_out_device_;
-  int audio_options_;
+  AudioOptions audio_options_;
   int audio_delay_offset_;
   int audio_output_volume_;
   std::string camera_device_;
diff --git a/session/media/channelmanager_unittest.cc b/session/media/channelmanager_unittest.cc
index 6f7c768..d0ba8b9 100644
--- a/session/media/channelmanager_unittest.cc
+++ b/session/media/channelmanager_unittest.cc
@@ -149,11 +149,12 @@
 }
 
 // Test that we can create and destroy a voice and video channel with a worker.
-// BUG=https://code.google.com/p/webrtc/issues/detail?id=2355
-TEST_F(ChannelManagerTest, DISABLED_CreateDestroyChannelsOnThread) {
+TEST_F(ChannelManagerTest, CreateDestroyChannelsOnThread) {
   worker_.Start();
   EXPECT_TRUE(cm_->set_worker_thread(&worker_));
   EXPECT_TRUE(cm_->Init());
+  delete session_;
+  session_ = new cricket::FakeSession(true, &worker_);
   cricket::VoiceChannel* voice_channel = cm_->CreateVoiceChannel(
       session_, cricket::CN_AUDIO, false);
   EXPECT_TRUE(voice_channel != NULL);
@@ -254,44 +255,43 @@
 
 TEST_F(ChannelManagerTest, SetAudioOptionsBeforeInit) {
   // Test that values that we set before Init are applied.
-  EXPECT_TRUE(cm_->SetAudioOptions("audio-in1", "audio-out1", 0x2));
+  AudioOptions options;
+  options.auto_gain_control.Set(true);
+  options.echo_cancellation.Set(false);
+  EXPECT_TRUE(cm_->SetAudioOptions("audio-in1", "audio-out1", options));
+  std::string audio_in, audio_out;
+  AudioOptions set_options;
+  // Check options before Init.
+  EXPECT_TRUE(cm_->GetAudioOptions(&audio_in, &audio_out, &set_options));
+  EXPECT_EQ("audio-in1", audio_in);
+  EXPECT_EQ("audio-out1", audio_out);
+  EXPECT_EQ(options, set_options);
   EXPECT_TRUE(cm_->Init());
-  EXPECT_EQ("audio-in1", fme_->audio_in_device());
-  EXPECT_EQ("audio-out1", fme_->audio_out_device());
-  EXPECT_EQ(0x2, fme_->audio_options());
-  EXPECT_EQ(0, fme_->audio_delay_offset());
+  // Check options after Init.
+  EXPECT_TRUE(cm_->GetAudioOptions(&audio_in, &audio_out, &set_options));
+  EXPECT_EQ("audio-in1", audio_in);
+  EXPECT_EQ("audio-out1", audio_out);
+  EXPECT_EQ(options, set_options);
+  // At this point, the media engine should also be initialized.
+  EXPECT_EQ(options, fme_->audio_options());
   EXPECT_EQ(cricket::MediaEngineInterface::kDefaultAudioDelayOffset,
             fme_->audio_delay_offset());
 }
 
-TEST_F(ChannelManagerTest, GetAudioOptionsBeforeInit) {
-  std::string audio_in, audio_out;
-  int opts;
-  // Test that GetAudioOptions works before Init.
-  EXPECT_TRUE(cm_->SetAudioOptions("audio-in2", "audio-out2", 0x1));
-  EXPECT_TRUE(cm_->GetAudioOptions(&audio_in, &audio_out, &opts));
-  EXPECT_EQ("audio-in2", audio_in);
-  EXPECT_EQ("audio-out2", audio_out);
-  EXPECT_EQ(0x1, opts);
-  // Test that options set before Init can be gotten after Init.
-  EXPECT_TRUE(cm_->SetAudioOptions("audio-in1", "audio-out1", 0x2));
-  EXPECT_TRUE(cm_->Init());
-  EXPECT_TRUE(cm_->GetAudioOptions(&audio_in, &audio_out, &opts));
-  EXPECT_EQ("audio-in1", audio_in);
-  EXPECT_EQ("audio-out1", audio_out);
-  EXPECT_EQ(0x2, opts);
-}
-
 TEST_F(ChannelManagerTest, GetAudioOptionsWithNullParameters) {
   std::string audio_in, audio_out;
-  int opts;
-  EXPECT_TRUE(cm_->SetAudioOptions("audio-in2", "audio-out2", 0x1));
+  AudioOptions options;
+  options.echo_cancellation.Set(true);
+  EXPECT_TRUE(cm_->SetAudioOptions("audio-in2", "audio-out2", options));
   EXPECT_TRUE(cm_->GetAudioOptions(&audio_in, NULL, NULL));
   EXPECT_EQ("audio-in2", audio_in);
   EXPECT_TRUE(cm_->GetAudioOptions(NULL, &audio_out, NULL));
   EXPECT_EQ("audio-out2", audio_out);
-  EXPECT_TRUE(cm_->GetAudioOptions(NULL, NULL, &opts));
-  EXPECT_EQ(0x1, opts);
+  AudioOptions out_options;
+  EXPECT_TRUE(cm_->GetAudioOptions(NULL, NULL, &out_options));
+  bool echo_cancellation = false;
+  EXPECT_TRUE(out_options.echo_cancellation.Get(&echo_cancellation));
+  EXPECT_TRUE(echo_cancellation);
 }
 
 TEST_F(ChannelManagerTest, SetAudioOptions) {
@@ -301,47 +301,22 @@
             fme_->audio_in_device());
   EXPECT_EQ(std::string(cricket::DeviceManagerInterface::kDefaultDeviceName),
             fme_->audio_out_device());
-  EXPECT_EQ(cricket::MediaEngineInterface::DEFAULT_AUDIO_OPTIONS,
-            fme_->audio_options());
-  EXPECT_EQ(cricket::MediaEngineInterface::kDefaultAudioDelayOffset,
-            fme_->audio_delay_offset());
-  // Test setting defaults.
-  EXPECT_TRUE(cm_->SetAudioOptions("", "",
-      cricket::MediaEngineInterface::DEFAULT_AUDIO_OPTIONS));
-  EXPECT_EQ("", fme_->audio_in_device());
-  EXPECT_EQ("", fme_->audio_out_device());
-  EXPECT_EQ(cricket::MediaEngineInterface::DEFAULT_AUDIO_OPTIONS,
-            fme_->audio_options());
   EXPECT_EQ(cricket::MediaEngineInterface::kDefaultAudioDelayOffset,
             fme_->audio_delay_offset());
   // Test setting specific values.
-  EXPECT_TRUE(cm_->SetAudioOptions("audio-in1", "audio-out1", 0x2));
+  AudioOptions options;
+  options.auto_gain_control.Set(true);
+  EXPECT_TRUE(cm_->SetAudioOptions("audio-in1", "audio-out1", options));
   EXPECT_EQ("audio-in1", fme_->audio_in_device());
   EXPECT_EQ("audio-out1", fme_->audio_out_device());
-  EXPECT_EQ(0x2, fme_->audio_options());
+  bool auto_gain_control = false;
+  EXPECT_TRUE(
+      fme_->audio_options().auto_gain_control.Get(&auto_gain_control));
+  EXPECT_TRUE(auto_gain_control);
   EXPECT_EQ(cricket::MediaEngineInterface::kDefaultAudioDelayOffset,
             fme_->audio_delay_offset());
   // Test setting bad values.
-  EXPECT_FALSE(cm_->SetAudioOptions("audio-in9", "audio-out2", 0x1));
-}
-
-TEST_F(ChannelManagerTest, GetAudioOptions) {
-  std::string audio_in, audio_out;
-  int opts;
-  // Test initial state.
-  EXPECT_TRUE(cm_->Init());
-  EXPECT_TRUE(cm_->GetAudioOptions(&audio_in, &audio_out, &opts));
-  EXPECT_EQ(std::string(cricket::DeviceManagerInterface::kDefaultDeviceName),
-            audio_in);
-  EXPECT_EQ(std::string(cricket::DeviceManagerInterface::kDefaultDeviceName),
-            audio_out);
-  EXPECT_EQ(cricket::MediaEngineInterface::DEFAULT_AUDIO_OPTIONS, opts);
-  // Test that we get back specific values that we set.
-  EXPECT_TRUE(cm_->SetAudioOptions("audio-in1", "audio-out1", 0x2));
-  EXPECT_TRUE(cm_->GetAudioOptions(&audio_in, &audio_out, &opts));
-  EXPECT_EQ("audio-in1", audio_in);
-  EXPECT_EQ("audio-out1", audio_out);
-  EXPECT_EQ(0x2, opts);
+  EXPECT_FALSE(cm_->SetAudioOptions("audio-in9", "audio-out2", options));
 }
 
 TEST_F(ChannelManagerTest, SetCaptureDeviceBeforeInit) {
@@ -380,7 +355,8 @@
 // device is plugged back, we use it.
 TEST_F(ChannelManagerTest, SetAudioOptionsUnplugPlug) {
   // Set preferences "audio-in1" and "audio-out1" before init.
-  EXPECT_TRUE(cm_->SetAudioOptions("audio-in1", "audio-out1", 0x2));
+  AudioOptions options;
+  EXPECT_TRUE(cm_->SetAudioOptions("audio-in1", "audio-out1", options));
   // Unplug device "audio-in1" and "audio-out1".
   std::vector<std::string> in_device_list, out_device_list;
   in_device_list.push_back("audio-in2");
diff --git a/session/media/mediasession.cc b/session/media/mediasession.cc
index 8561230..799fe5f 100644
--- a/session/media/mediasession.cc
+++ b/session/media/mediasession.cc
@@ -38,12 +38,17 @@
 #include "talk/base/stringutils.h"
 #include "talk/media/base/constants.h"
 #include "talk/media/base/cryptoparams.h"
-#include "talk/media/sctp/sctpdataengine.h"
 #include "talk/p2p/base/constants.h"
 #include "talk/session/media/channelmanager.h"
 #include "talk/session/media/srtpfilter.h"
 #include "talk/xmpp/constants.h"
 
+#ifdef HAVE_SCTP
+#include "talk/media/sctp/sctpdataengine.h"
+#else
+static const uint32 kMaxSctpSid = USHRT_MAX;
+#endif
+
 namespace {
 const char kInline[] = "inline:";
 }
diff --git a/session/media/mediasessionclient.h b/session/media/mediasessionclient.h
index 1ade753..d0034ca 100644
--- a/session/media/mediasessionclient.h
+++ b/session/media/mediasessionclient.h
@@ -112,8 +112,8 @@
   }
 
   bool SetAudioOptions(const std::string& in_name, const std::string& out_name,
-                       int opts) {
-    return channel_manager_->SetAudioOptions(in_name, out_name, opts);
+                       const AudioOptions& options) {
+    return channel_manager_->SetAudioOptions(in_name, out_name, options);
   }
   bool SetOutputVolume(int level) {
     return channel_manager_->SetOutputVolume(level);
diff --git a/xmpp/mucroomdiscoverytask.cc b/xmpp/mucroomdiscoverytask.cc
index a5055d2..c7477ae 100644
--- a/xmpp/mucroomdiscoverytask.cc
+++ b/xmpp/mucroomdiscoverytask.cc
@@ -56,11 +56,11 @@
   const std::string name(identity->Attr(QN_NAME));
 
   // Get the conversation id
-  const XmlElement* convIdElement =
+  const XmlElement* conversation =
       identity->FirstNamed(QN_GOOGLE_MUC_HANGOUT_CONVERSATION_ID);
   std::string conversation_id;
-  if (convIdElement != NULL) {
-    conversation_id = convIdElement->BodyText();
+  if (conversation != NULL) {
+    conversation_id = conversation->BodyText();
   }
 
   for (const XmlElement* feature = query->FirstNamed(QN_DISCO_FEATURE);