Fix thread safety in XmlElement.

git-svn-id: http://libjingle.googlecode.com/svn/trunk@76 dd674b97-3498-5ee5-1854-bdd07cd0ff33
diff --git a/talk/base/basictypes.h b/talk/base/basictypes.h
index a160ee9..d34cb39 100644
--- a/talk/base/basictypes.h
+++ b/talk/base/basictypes.h
@@ -28,6 +28,8 @@
 #ifndef TALK_BASE_BASICTYPES_H__
 #define TALK_BASE_BASICTYPES_H__
 
+#include <stddef.h>  // for NULL, size_t
+
 #ifndef WIN32
 #include <stdint.h>  // for uintptr_t
 #endif
diff --git a/talk/base/criticalsection.h b/talk/base/criticalsection.h
index ee3db1a..07cf5b2 100644
--- a/talk/base/criticalsection.h
+++ b/talk/base/criticalsection.h
@@ -126,6 +126,35 @@
   CriticalSection *pcrit_;
 };
 
+// TODO: Replace with platform-specific "atomic" ops.
+// Something like: google3/base/atomicops.h TODO: And, move
+// it to atomicops.h, which can't be done easily because of complex
+// compile rules.
+class AtomicOps {
+ public:
+  static int Increment(int* i) {
+    // Could be faster, and less readable:
+    // static CriticalSection* crit = StaticCrit();
+    // CritScope scope(crit);
+    CritScope scope(StaticCrit());
+    return ++(*i);
+  }
+
+  static int Decrement(int* i) {
+    // Could be faster, and less readable:
+    // static CriticalSection* crit = StaticCrit();
+    // CritScope scope(crit);
+    CritScope scope(StaticCrit());
+    return --(*i);
+  }
+
+ private:
+  static CriticalSection* StaticCrit() {
+    static CriticalSection* crit = new CriticalSection();
+    return crit;
+  }
+};
+
 } // namespace talk_base
 
 #endif // TALK_BASE_CRITICALSECTION_H__
diff --git a/talk/base/latebindingsymboltable.h b/talk/base/latebindingsymboltable.h
index 994c26a..3a9544f 100644
--- a/talk/base/latebindingsymboltable.h
+++ b/talk/base/latebindingsymboltable.h
@@ -28,7 +28,6 @@
 #ifndef TALK_BASE_LATEBINDINGSYMBOLTABLE_H_
 #define TALK_BASE_LATEBINDINGSYMBOLTABLE_H_
 
-#include <stddef.h>  // for NULL
 #include <string.h>
 
 #include "talk/base/common.h"
diff --git a/talk/base/macutils.cc b/talk/base/macutils.cc
index fefe2b9..21d31ff 100644
--- a/talk/base/macutils.cc
+++ b/talk/base/macutils.cc
@@ -62,6 +62,7 @@
   return (NULL != *str16);
 }
 
+#ifdef OSX
 void DecodeFourChar(UInt32 fc, std::string* out) {
   std::stringstream ss;
   ss << '\'';
@@ -187,7 +188,7 @@
   }
 
   err = OSAExecute(component, script_id, kOSANullScript, kOSAModeCanInteract,
-		   &result_id);
+                   &result_id);
 
   if (err == errOSAScriptError) {
     LOG(LS_ERROR) << "Error when executing Apple Script: " << script;
@@ -213,7 +214,7 @@
   CloseComponent(component);
   return true;
 }
-
+#endif
 
 ///////////////////////////////////////////////////////////////////////////////
 
diff --git a/talk/base/macutils.h b/talk/base/macutils.h
index 30bb300..be6e728 100644
--- a/talk/base/macutils.h
+++ b/talk/base/macutils.h
@@ -29,16 +29,22 @@
 #define TALK_BASE_MACUTILS_H__
 
 #include <CoreFoundation/CoreFoundation.h>
+#ifdef OSX
 #include <Carbon/Carbon.h>
+#endif
 #include <string>
 
 namespace talk_base {
 
 ///////////////////////////////////////////////////////////////////////////////
 
+// Note that some of these functions work for both iOS and Mac OS X.  The ones
+// that are specific to Mac are #ifdef'ed as such.
+
 bool ToUtf8(const CFStringRef str16, std::string* str8);
 bool ToUtf16(const std::string& str8, CFStringRef* str16);
 
+#ifdef OSX
 void DecodeFourChar(UInt32 fc, std::string* out);
 std::string DecodeEvent(EventRef event);
 
@@ -58,6 +64,7 @@
 // Runs the given apple script. Only supports scripts that does not
 // require user interaction.
 bool RunAppleScript(const std::string& script);
+#endif
 
 ///////////////////////////////////////////////////////////////////////////////
 
diff --git a/talk/base/thread.cc b/talk/base/thread.cc
index 609d965..1603ef6 100644
--- a/talk/base/thread.cc
+++ b/talk/base/thread.cc
@@ -174,7 +174,8 @@
 #if defined(WIN32)
       thread_(NULL),
 #endif
-      owned_(true) {
+      owned_(true),
+      delete_self_when_complete_(false) {
   g_thmgr.Add(this);
   SetName("Thread", this);  // default name
 }
@@ -367,6 +368,10 @@
   } else {
     init->thread->Run();
   }
+  if (init->thread->delete_self_when_complete_) {
+    init->thread->started_ = false;
+    delete init->thread;
+  }
   delete init;
   return NULL;
 }
diff --git a/talk/base/thread.h b/talk/base/thread.h
index efbdae7..50d8eeb 100644
--- a/talk/base/thread.h
+++ b/talk/base/thread.h
@@ -151,6 +151,12 @@
   bool started() const { return started_; }
   bool Start(Runnable* runnable = NULL);
 
+  // Used for fire-and-forget threads.  Deletes this thread object when the
+  // Run method returns.
+  void Release() {
+    delete_self_when_complete_ = true;
+  }
+
   // Tells the thread to stop and waits until it is joined.
   // Never call Stop on the current thread.  Instead use the inherited Quit
   // function which will exit the base MessageQueue without terminating the
@@ -215,6 +221,7 @@
 #endif
 
   bool owned_;
+  bool delete_self_when_complete_;
 
   friend class ThreadManager;
 
diff --git a/talk/base/unixfilesystem.cc b/talk/base/unixfilesystem.cc
index 2a243ba..096c511 100644
--- a/talk/base/unixfilesystem.cc
+++ b/talk/base/unixfilesystem.cc
@@ -61,36 +61,24 @@
 #include "talk/base/stream.h"
 #include "talk/base/stringutils.h"
 
-#ifdef ANDROID
-namespace {
-// Android does not have a concept of a single temp dir shared by all
-// because resource are scarse on a phone. Instead each app gets some
-// space on the sdcard under a path that is given at runtime by the
-// system.
-// The disk allocation feature is still work in progress so currently
-// we return a hardcoded a path on the sdcard. In the future, we
-// should do a JNI call to get that info from the context.
-// TODO: Replace hardcoded path with a query to the Context
-// object to get the equivalents of '/tmp' and '~/.'
-
-// @return the folder for libjingle. Some extra path (typically
-// Google/<app name>) will be added.
-const char* GetAndroidAppDataFolder() {
-  return "/sdcard";
-}
-
-// @return the tmp folder to be used. Some extra path will be added to
-// that base folder.
-const char* GetAndroidTempFolder() {
-  return "/sdcard";
-}
-
-}  // anonymous namespace
-#endif
-
 namespace talk_base {
 
-std::string UnixFilesystem::app_temp_path_;
+#if !defined(ANDROID) && !defined(IOS)
+char* UnixFilesystem::app_temp_path_ = NULL;
+#else
+char* UnixFilesystem::provided_app_data_folder_ = NULL;
+char* UnixFilesystem::provided_app_temp_folder_ = NULL;
+
+void UnixFilesystem::SetAppDataFolder(const std::string& folder) {
+  delete [] provided_app_data_folder_;
+  provided_app_data_folder_ = CopyString(folder);
+}
+
+void UnixFilesystem::SetAppTempFolder(const std::string& folder) {
+  delete [] provided_app_temp_folder_;
+  provided_app_temp_folder_ = CopyString(folder);
+}
+#endif
 
 bool UnixFilesystem::CreateFolder(const Pathname &path) {
   std::string pathname(path.pathname());
@@ -179,8 +167,9 @@
   if (0 != FSRefMakePath(&fr, buffer, ARRAY_SIZE(buffer)))
     return false;
   pathname.SetPathname(reinterpret_cast<char*>(buffer), "");
-#elif defined(ANDROID)
-  pathname.SetPathname(GetAndroidTempFolder(), "");
+#elif defined(ANDROID) || defined(IOS)
+  ASSERT(provided_app_temp_folder_ != NULL);
+  pathname.SetPathname(provided_app_temp_folder_, "");
 #else  // !OSX && !ANDROID
   if (const char* tmpdir = getenv("TMPDIR")) {
     pathname.SetPathname(tmpdir, "");
@@ -288,15 +277,19 @@
 }
 
 bool UnixFilesystem::IsTemporaryPath(const Pathname& pathname) {
+#if defined(ANDROID) || defined(IOS)
+  ASSERT(provided_app_temp_folder_ != NULL);
+#endif
+
   const char* const kTempPrefixes[] = {
-#ifdef ANDROID
-    GetAndroidTempFolder()
+#if defined(ANDROID) || defined(IOS)
+    provided_app_temp_folder_,
 #else
     "/tmp/", "/var/tmp/",
 #ifdef OSX
     "/private/tmp/", "/private/var/tmp/", "/private/var/folders/",
 #endif  // OSX
-#endif  // ANDROID
+#endif  // ANDROID || IOS
   };
   for (size_t i = 0; i < ARRAY_SIZE(kTempPrefixes); ++i) {
     if (0 == strncmp(pathname.pathname().c_str(), kTempPrefixes[i],
@@ -396,11 +389,10 @@
     // TODO
     return false;
   }
-#elif defined(ANDROID)  // && !OSX
-  // TODO: Check if the new disk allocation mechanism works
-  // per-user and we don't have the per_user distinction.
-  path->SetPathname(GetAndroidAppDataFolder(), "");
-#elif defined(LINUX)  // && !OSX && !defined(ANDROID)
+#elif defined(ANDROID) || defined(IOS)  // && !OSX
+  ASSERT(provided_app_data_folder_ != NULL);
+  path->SetPathname(provided_app_data_folder_, "");
+#elif defined(LINUX)  // && !OSX && !defined(ANDROID) && !defined(IOS)
   if (per_user) {
     // We follow the recommendations in
     // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
@@ -434,7 +426,7 @@
 #endif  // !OSX && !defined(ANDROID) && !defined(LINUX)
 
   // Now add on a sub-path for our app.
-#if defined(OSX) || defined(ANDROID)
+#if defined(OSX) || defined(ANDROID) || defined(IOS)
   path->AppendFolder(organization_name_);
   path->AppendFolder(application_name_);
 #elif defined(LINUX)
@@ -452,9 +444,14 @@
 }
 
 bool UnixFilesystem::GetAppTempFolder(Pathname* path) {
+#if defined(ANDROID) || defined(IOS)
+  ASSERT(provided_app_temp_folder_ != NULL);
+  path->SetPathname(provided_app_temp_folder_);
+  return true;
+#else
   ASSERT(!application_name_.empty());
   // TODO: Consider whether we are worried about thread safety.
-  if (!app_temp_path_.empty()) {
+  if (app_temp_path_ != NULL && strlen(app_temp_path_) > 0) {
     path->SetPathname(app_temp_path_);
     return true;
   }
@@ -469,9 +466,11 @@
   if (!GetTemporaryFolder(*path, true, &folder))
     return false;
 
-  app_temp_path_ = path->pathname();
+  delete [] app_temp_path_;
+  app_temp_path_ = CopyString(path->pathname());
   // TODO: atexit(DeleteFolderAndContents(app_temp_path_));
   return true;
+#endif
 }
 
 bool UnixFilesystem::GetDiskFreeSpace(const Pathname& path, int64 *freebytes) {
@@ -519,4 +518,16 @@
   return cwd;
 }
 
+char* UnixFilesystem::CopyString(const std::string& str) {
+  size_t size = str.length() + 1;
+
+  char* buf = new char[size];
+  if (!buf) {
+    return NULL;
+  }
+
+  strcpyn(buf, size, str.c_str());
+  return buf;
+}
+
 }  // namespace talk_base
diff --git a/talk/base/unixfilesystem.h b/talk/base/unixfilesystem.h
index 71960ac..e443dc8 100644
--- a/talk/base/unixfilesystem.h
+++ b/talk/base/unixfilesystem.h
@@ -34,7 +34,17 @@
 
 class UnixFilesystem : public FilesystemInterface {
  public:
- 
+
+#if defined(ANDROID) || defined(IOS)
+// Android does not have a native code API to fetch the app data or temp
+// folders. That needs to be passed into this class from Java. Similarly, iOS
+// only supports an Objective-C API for fetching the folder locations, so that
+// needs to be passed in here from Objective-C.
+
+  static void SetAppDataFolder(const std::string& folder);
+  static void SetAppTempFolder(const std::string& folder);
+#endif
+
   // Opens a file. Returns an open StreamInterface if function succeeds. Otherwise,
   // returns NULL.
   virtual FileStream *OpenFile(const Pathname &filename, 
@@ -106,7 +116,14 @@
   virtual Pathname GetCurrentDirectory();
 
  private:
-  static std::string app_temp_path_;
+#if defined(ANDROID) || defined(IOS)
+  static char* provided_app_data_folder_;
+  static char* provided_app_temp_folder_;
+#else
+  static char* app_temp_path_;
+#endif
+
+  static char* CopyString(const std::string& str);
 };
 
 }  // namespace talk_base
diff --git a/talk/libjingle.scons b/talk/libjingle.scons
index 44259de..10b401a 100644
--- a/talk/libjingle.scons
+++ b/talk/libjingle.scons
@@ -70,6 +70,13 @@
                "sound/soundsysteminterface.cc",
                "sound/soundsystemproxy.cc",
              ],
+             dependent_target_settings = {
+               'lin_libs': [
+                 "dl",
+                 "pthread",
+                 "rt",
+               ],
+             },
              mac_srcs = [
                "base/macasyncsocket.cc",
                "base/macconversion.cc",
@@ -232,44 +239,8 @@
                "base/winping.cc",
                "session/phone/gdivideorenderer.cc",
              ],
-)
-talk.App(env, name = "login",
-         libs = [
-           "libjingle",
-           "expat",
-           "libxmpphelp",
-         ],
-         srcs = [
-           "examples/login/xmppthread.cc",
-           "examples/login/login_main.cc",
-         ],
-         mac_libs = [
-           "crypto",
-           "ssl",
-         ],
-         lin_libs = [
-           "libpthread",
-           ":libssl.so.0.9.8",
-           "gdk_pixbuf-2.0",
-           "gdk-x11-2.0",
-           "glib-2.0",
-           "gobject-2.0",
-           "gtk-x11-2.0",
-           "videorenderer",
-         ],
-)
-talk.Library(env, name = "videorenderer",
-             lin_srcs = [
-               "session/phone/gtkvideorenderer.cc",
-             ],
-             lin_includedirs = [
-               "/usr/include/atk-1.0",
-               "/usr/include/cairo",
-               "/usr/include/glib-2.0",
-               "/usr/include/gtk-2.0",
-               "/usr/include/pango-1.0",
-               "/usr/lib/glib-2.0/include",
-               "/usr/lib/gtk-2.0/include",
+             extra_srcs = [
+               "base/json.cc",
              ],
 )
 talk.Library(env, name = "libxmpphelp",
@@ -282,6 +253,32 @@
                "examples/login/xmppsocket.cc",
              ],
 )
+talk.Library(env, name = "videorenderer",
+             lin_srcs = [
+               "session/phone/gtkvideorenderer.cc",
+             ],
+             lin_packages = [
+               "gtk+-2.0",
+             ],
+)
+talk.App(env, name = "login",
+         libs = [
+           "libjingle",
+           "expat",
+           "libxmpphelp",
+         ],
+         srcs = [
+           "examples/login/xmppthread.cc",
+           "examples/login/login_main.cc",
+         ],
+         posix_libs = [
+           "crypto",
+           "ssl",
+         ],
+         lin_libs = [
+           "videorenderer",
+         ],
+)
 talk.App(env, name = "call",
          mac_frameworks = [
            "AudioToolbox",
@@ -300,7 +297,7 @@
            "strmiids.lib",
            "winmm.lib",
          ],
-         mac_libs = [
+         posix_libs = [
            "crypto",
            "ssl",
          ],
@@ -308,14 +305,6 @@
            "FEATURE_ENABLE_VOICEMAIL",
          ],
          lin_libs = [
-           "gdk_pixbuf-2.0",
-           "gdk-x11-2.0",
-           "glib-2.0",
-           "gobject-2.0",
-           "gtk-x11-2.0",
-           "libasound",
-           "libpthread",
-           ":libssl.so.0.9.8",
            "videorenderer",
          ],
          srcs = [
@@ -344,9 +333,6 @@
          srcs = [
            "p2p/base/relayserver_main.cc",
          ],
-         lin_libs = [
-           "libpthread",
-         ],
 )
 talk.App(env, name = "stunserver",
          libs = [
@@ -355,7 +341,4 @@
          srcs = [
            "p2p/base/stunserver_main.cc",
          ],
-         lin_libs = [
-           "libpthread",
-         ],
 )
diff --git a/talk/main.scons b/talk/main.scons
index 9289dcf..de1d84b 100644
--- a/talk/main.scons
+++ b/talk/main.scons
@@ -24,6 +24,11 @@
 # If the name is the name of a directory then that directory shall contain a
 # .scons file with the same name as the directory itself:
 #  Ex: The directory session/phone contains a file called phone.scons
+# This list must be in order of library dependencies. e.g., if
+# session/phone/phone.scons defines a target that links to a library target
+# defined in sound/sound.scons, then 'sound' must come first.
+# When no particular order is imposed by library dependencies, try to keep in
+# mostly alphabetical order.
 #
 components = talk.Components("libjingle.scons")
 
@@ -41,7 +46,7 @@
     'component_setup',
     'replace_strings',
     'talk_noops',
-    #'talk_linux',
+    #'talk_utils',
   ],
   BUILD_SCONSCRIPTS = components,
   DESTINATION_ROOT = '$MAIN_DIR/build',
@@ -361,8 +366,7 @@
 linux_common_env = posix_env.Clone(
   tools = [
     'target_platform_linux',
-    'talk_libjingle',
-    #'talk_linux',
+    'talk_linux',
   ],
 )
 
@@ -383,6 +387,8 @@
   LINKFLAGS = [
     # Enable dead-code removal.
     '-Wl,--gc-sections',
+    # Elide dependencies on shared libraries that we're not actually using.
+    '-Wl,--as-needed',
     '-Wl,--start-group',
   ],
   _LIBFLAGS = ['-Wl,--end-group'],
diff --git a/talk/p2p/base/port.cc b/talk/p2p/base/port.cc
index 800b9b9..7740336 100644
--- a/talk/p2p/base/port.cc
+++ b/talk/p2p/base/port.cc
@@ -134,6 +134,7 @@
       ip_(ip),
       min_port_(min_port),
       max_port_(max_port),
+      generation_(0),
       preference_(-1),
       lifetime_(LT_PRESTART),
       enable_port_packets_(false) {
diff --git a/talk/p2p/base/pseudotcp.cc b/talk/p2p/base/pseudotcp.cc
index 955584e..685a39f 100644
--- a/talk/p2p/base/pseudotcp.cc
+++ b/talk/p2p/base/pseudotcp.cc
@@ -231,7 +231,7 @@
   m_rcv_wnd = sizeof(m_rbuf);
   m_snd_nxt = m_slen = 0;
   m_snd_wnd = 1;
-  m_snd_una = m_rcv_nxt = m_rlen = 0;
+  m_snd_una = m_rcv_nxt = m_rlen = m_rpos = 0;
   m_bReadEnable = true;
   m_bWriteEnable = false;
   m_t_ack = 0;
@@ -400,24 +400,23 @@
     return SOCKET_ERROR;
   }
 
-  if (m_rlen == 0) {
+  // Make sure read position is correct.
+  ASSERT(m_rpos <= m_rlen);
+  if (m_rlen == m_rpos) {
     m_bReadEnable = true;
     m_error = EWOULDBLOCK;
     return SOCKET_ERROR;
   }
 
-  uint32 read = talk_base::_min(uint32(len), m_rlen);
-  memcpy(buffer, m_rbuf, read);
-  m_rlen -= read;
+  uint32 read = talk_base::_min(uint32(len), m_rlen - m_rpos);
+  memcpy(buffer, m_rbuf + m_rpos, read);
+  m_rpos += read;
 
-  // !?! until we create a circular buffer, we need to move all of the rest of the buffer up!
-  memmove(m_rbuf, m_rbuf + read, sizeof(m_rbuf) - read/*m_rlen*/);
-
-  if ((sizeof(m_rbuf) - m_rlen - m_rcv_wnd)
-      >= talk_base::_min<uint32>(sizeof(m_rbuf) / 2, m_mss)) {
+  if (getReceiveBufferSpace() - m_rcv_wnd >=
+      talk_base::_min<uint32>(sizeof(m_rbuf) / 2, m_mss)) {
     bool bWasClosed = (m_rcv_wnd == 0); // !?! Not sure about this was closed business
 
-    m_rcv_wnd = sizeof(m_rbuf) - m_rlen;
+    m_rcv_wnd = getReceiveBufferSpace();
 
     if (bWasClosed) {
       attemptSend(sfImmediateAck);
@@ -824,8 +823,8 @@
       seg.len = 0;
     }
   }
-  if ((seg.seq + seg.len - m_rcv_nxt) > (sizeof(m_rbuf) - m_rlen)) {
-    uint32 nAdjust = seg.seq + seg.len - m_rcv_nxt - (sizeof(m_rbuf) - m_rlen);
+  if ((seg.seq + seg.len - m_rcv_nxt) > getReceiveBufferSpace()) {
+    uint32 nAdjust = seg.seq + seg.len - m_rcv_nxt - getReceiveBufferSpace();
     if (nAdjust < seg.len) {
       seg.len -= nAdjust;
     } else {
@@ -843,6 +842,12 @@
       }
     } else {
       uint32 nOffset = seg.seq - m_rcv_nxt;
+
+      if (getReceiveBufferConsecutiveSpace() < seg.len + nOffset) {
+        consolidateReceiveBufferSpace();
+        ASSERT(getReceiveBufferConsecutiveSpace() >= seg.len + nOffset);
+      }
+
       memcpy(m_rbuf + m_rlen + nOffset, seg.data, seg.len);
       if (seg.seq == m_rcv_nxt) {
         m_rlen += seg.len;
@@ -1082,4 +1087,26 @@
   m_cwnd = talk_base::_max(m_cwnd, m_mss);
 }
 
+bool
+PseudoTcp::isReceiveBufferFull() const {
+  return !getReceiveBufferSpace();
+}
+
+uint32
+PseudoTcp::getReceiveBufferSpace() const {
+  return sizeof(m_rbuf) - m_rlen + m_rpos;
+}
+
+uint32
+PseudoTcp::getReceiveBufferConsecutiveSpace() const {
+  return sizeof(m_rbuf) - m_rlen;
+}
+
+void
+PseudoTcp::consolidateReceiveBufferSpace() {
+  memmove(m_rbuf, m_rbuf + m_rpos, sizeof(m_rbuf) - m_rpos);
+  m_rlen -= m_rpos;
+  m_rpos = 0;
+}
+
 }  // namespace cricket
diff --git a/talk/p2p/base/pseudotcp.h b/talk/p2p/base/pseudotcp.h
index e8e0d3a..fd11c86 100644
--- a/talk/p2p/base/pseudotcp.h
+++ b/talk/p2p/base/pseudotcp.h
@@ -155,7 +155,20 @@
 
   void adjustMTU();
 
+ protected:
+  // This method is used in test only to query receive buffer state.
+  bool isReceiveBufferFull() const;
+
  private:
+  // Get the total number of bytes of free space in m_rbuf, consecutive or not.
+  uint32 getReceiveBufferSpace() const;
+
+  // Get the number of bytes that can be written to m_rbuf.
+  uint32 getReceiveBufferConsecutiveSpace() const;
+
+  // Consolidate free space in m_rbuf so that it is a consecutive segment.
+  void consolidateReceiveBufferSpace();
+
   IPseudoTcpNotify* m_notify;
   enum Shutdown { SD_NONE, SD_GRACEFUL, SD_FORCEFUL } m_shutdown;
   int m_error;
@@ -170,7 +183,7 @@
   typedef std::list<RSegment> RList;
   RList m_rlist;
   char m_rbuf[kRcvBufSize];
-  uint32 m_rcv_nxt, m_rcv_wnd, m_rlen, m_lastrecv;
+  uint32 m_rcv_nxt, m_rcv_wnd, m_rpos, m_rlen, m_lastrecv;
 
   // Outgoing data
   SList m_slist;
diff --git a/talk/p2p/client/httpportallocator.cc b/talk/p2p/client/httpportallocator.cc
index 066a249..1e5e256 100644
--- a/talk/p2p/client/httpportallocator.cc
+++ b/talk/p2p/client/httpportallocator.cc
@@ -150,6 +150,11 @@
 }
 
 void HttpPortAllocatorSession::TryCreateRelaySession() {
+  if (allocator()->flags() & PORTALLOCATOR_DISABLE_RELAY) {
+    LOG(LS_VERBOSE) << "HttpPortAllocator: Relay ports disabled, skipping.";
+    return;
+  }
+
   if (attempts_ == HttpPortAllocator::kNumRetries) {
     LOG(LS_ERROR) << "HttpPortAllocator: maximum number of requests reached; "
                   << "giving up on relay.";
diff --git a/talk/session/phone/channel.cc b/talk/session/phone/channel.cc
index 05366a5..f0b8e79 100644
--- a/talk/session/phone/channel.cc
+++ b/talk/session/phone/channel.cc
@@ -34,7 +34,6 @@
 #include "talk/p2p/base/transportchannel.h"
 #include "talk/session/phone/channelmanager.h"
 #include "talk/session/phone/mediasessionclient.h"
-#include "talk/session/phone/mediasink.h"
 #include "talk/session/phone/rtcpmuxfilter.h"
 #include "talk/session/phone/rtputils.h"
 
@@ -81,8 +80,6 @@
       media_engine_(media_engine),
       session_(session),
       media_channel_(media_channel),
-      received_media_sink_(NULL),
-      sent_media_sink_(NULL),
       content_name_(content_name),
       transport_channel_(transport_channel),
       rtcp_transport_channel_(NULL),
@@ -280,19 +277,6 @@
     return false;
   }
 
-  // Push the packet down to the media sink.
-  // Need to do this before protecting the packet.
-  {
-    talk_base::CritScope cs(&sink_critical_section_);
-    if (sent_media_sink_) {
-      if (!rtcp) {
-        sent_media_sink_->OnRtpPacket(packet->data(), packet->length());
-      } else {
-        sent_media_sink_->OnRtcpPacket(packet->data(), packet->length());
-      }
-    }
-  }
-
   // Protect if needed.
   if (srtp_filter_.IsActive()) {
     bool res;
@@ -325,6 +309,13 @@
     packet->SetLength(len);
   }
 
+  // Signal to the media sink after protecting the packet. TODO:
+  // Separate APIs to record unprotected media and protected header.
+  {
+    talk_base::CritScope cs(&signal_send_packet_cs_);
+    SignalSendPacket(packet->data(), packet->length(), rtcp);
+  }
+
   // Bon voyage.
   return (channel->SendPacket(packet->data(), packet->length())
       == static_cast<int>(packet->length()));
@@ -339,6 +330,13 @@
     return;
   }
 
+  // Signal to the media sink before unprotecting the packet. TODO:
+  // Separate APIs to record unprotected media and protected header.
+  {
+    talk_base::CritScope cs(&signal_recv_packet_cs_);
+    SignalRecvPacket(packet->data(), packet->length(), rtcp);
+  }
+
   // Unprotect the packet, if needed.
   if (srtp_filter_.IsActive()) {
     char* data = packet->data();
@@ -376,18 +374,6 @@
   } else {
     media_channel_->OnRtcpReceived(packet);
   }
-
-  // Push it down to the media sink.
-  {
-    talk_base::CritScope cs(&sink_critical_section_);
-    if (received_media_sink_) {
-      if (!rtcp) {
-        received_media_sink_->OnRtpPacket(packet->data(), packet->length());
-      } else {
-        received_media_sink_->OnRtcpPacket(packet->data(), packet->length());
-      }
-    }
-  }
 }
 
 void BaseChannel::OnSessionState(BaseSession* session,
diff --git a/talk/session/phone/channel.h b/talk/session/phone/channel.h
index 241ca79..baf07a9 100644
--- a/talk/session/phone/channel.h
+++ b/talk/session/phone/channel.h
@@ -47,7 +47,6 @@
 namespace cricket {
 
 class MediaContentDescription;
-class MediaSinkInterface;
 struct CryptoParams;
 
 enum {
@@ -118,27 +117,46 @@
   void StartConnectionMonitor(int cms);
   void StopConnectionMonitor();
 
-  // Set and get media sinks for recording media.
-  void set_received_media_sink(MediaSinkInterface* sink) {
-    talk_base::CritScope cs(&sink_critical_section_);
-    received_media_sink_ = sink;
-  }
-  const MediaSinkInterface* received_media_sink() {
-    talk_base::CritScope cs(&sink_critical_section_);
-    return received_media_sink_;
-  }
-  void set_sent_media_sink(MediaSinkInterface* sink) {
-    talk_base::CritScope cs(&sink_critical_section_);
-    sent_media_sink_ = sink;
-  }
-  const MediaSinkInterface* sent_media_sink() {
-    talk_base::CritScope cs(&sink_critical_section_);
-    return sent_media_sink_;
-  }
   void set_srtp_signal_silent_time(uint32 silent_time) {
     srtp_filter_.set_signal_silent_time(silent_time);
   }
 
+  template <class T>
+  void RegisterSendSink(T* sink,
+                        void (T::*OnPacket)(const void*, size_t, bool)) {
+    talk_base::CritScope cs(&signal_send_packet_cs_);
+    SignalSendPacket.disconnect(sink);
+    SignalSendPacket.connect(sink, OnPacket);
+  }
+
+  void UnregisterSendSink(sigslot::has_slots<>* sink) {
+    talk_base::CritScope cs(&signal_send_packet_cs_);
+    SignalSendPacket.disconnect(sink);
+  }
+
+  bool HasSendSinks() {
+    talk_base::CritScope cs(&signal_send_packet_cs_);
+    return !SignalSendPacket.is_empty();
+  }
+
+  template <class T>
+  void RegisterRecvSink(T* sink,
+                        void (T::*OnPacket)(const void*, size_t, bool)) {
+    talk_base::CritScope cs(&signal_recv_packet_cs_);
+    SignalRecvPacket.disconnect(sink);
+    SignalRecvPacket.connect(sink, OnPacket);
+  }
+
+  void UnregisterRecvSink(sigslot::has_slots<>* sink) {
+    talk_base::CritScope cs(&signal_recv_packet_cs_);
+    SignalRecvPacket.disconnect(sink);
+  }
+
+  bool HasRecvSinks() {
+    talk_base::CritScope cs(&signal_recv_packet_cs_);
+    return !SignalRecvPacket.is_empty();
+  }
+
  protected:
   MediaEngine* media_engine() const { return media_engine_; }
   virtual MediaChannel* media_channel() const { return media_channel_; }
@@ -238,15 +256,15 @@
       const std::vector<ConnectionInfo> &infos) = 0;
 
  private:
+  sigslot::signal3<const void*, size_t, bool> SignalSendPacket;
+  sigslot::signal3<const void*, size_t, bool> SignalRecvPacket;
+  talk_base::CriticalSection signal_send_packet_cs_;
+  talk_base::CriticalSection signal_recv_packet_cs_;
+
   talk_base::Thread *worker_thread_;
   MediaEngine *media_engine_;
   BaseSession *session_;
   MediaChannel *media_channel_;
-  // Media sinks to handle the received or sent RTP/RTCP packets. These are
-  // reference to the objects owned by the media recorder.
-  MediaSinkInterface* received_media_sink_;
-  MediaSinkInterface* sent_media_sink_;
-  talk_base::CriticalSection sink_critical_section_;
 
   std::string content_name_;
   TransportChannel *transport_channel_;
diff --git a/talk/session/phone/mediaengine.cc b/talk/session/phone/mediaengine.cc
index f483d1b..e6b663d 100644
--- a/talk/session/phone/mediaengine.cc
+++ b/talk/session/phone/mediaengine.cc
@@ -32,37 +32,20 @@
 #elif defined(HAVE_WEBRTC)
 #include "talk/session/phone/webrtcvoiceengine.h"
 #include "talk/session/phone/webrtcvideoengine.h"
-#if defined(PLATFORM_CHROMIUM)
-#include "content/renderer/renderer_webrtc_audio_device_impl.h"
-#else  // Other browsers
-#endif  // PLATFORM_CHROMIUM
 #else
 #endif  // HAVE_LINPHONE
 
 namespace cricket {
-#if defined(PLATFORM_CHROMIUM)
-class ChromiumWebRtcVoiceEngine : public WebRtcVoiceEngine {
- public:
-  // TODO: where should we get the AudioDevice initial configuration
-  ChromiumWebRtcVoiceEngine() : WebRtcVoiceEngine(
-      new RendererWebRtcAudioDeviceImpl(1440, 1440, 1, 1, 48000, 48000)) {}
-};
-#else  // Other browsers
-#endif  // PLATFORM_CHROMIUM
-
 MediaEngine* MediaEngine::Create() {
 #if defined(HAVE_LINPHONE)
   return new LinphoneMediaEngine("", "");
-#elif defined(HAVE_WEBRTC) && defined(PLATFORM_CHROMIUM)
-  return new CompositeMediaEngine<ChromiumWebRtcVoiceEngine,
-      WebRtcVideoEngine>();
 #elif defined(HAVE_WEBRTC)
   return new CompositeMediaEngine<WebRtcVoiceEngine, WebRtcVideoEngine>();
 #elif defined(ANDROID)
   return AndroidMediaEngineFactory::Create();
 #else
   return new NullMediaEngine();
-#endif  // HAVE_LINPHONE HAVE_WEBRTC PLATFORM_CHROMIUM ANDROID
+#endif  // HAVE_LINPHONE HAVE_WEBRTC ANDROID
 }
 
 };  // namespace cricket
diff --git a/talk/session/phone/mediasink.h b/talk/session/phone/mediasink.h
index 078b534..3bc12bc 100644
--- a/talk/session/phone/mediasink.h
+++ b/talk/session/phone/mediasink.h
@@ -31,8 +31,7 @@
 namespace cricket {
 
 // MediaSinkInterface is a sink to handle RTP and RTCP packets that are sent or
-// received by a channel. Each channel needs two MediaSinkInterface, one for
-// the sent packets and the other for the received packets.
+// received by a channel.
 class MediaSinkInterface {
  public:
   virtual ~MediaSinkInterface() {}
@@ -40,8 +39,8 @@
   virtual void SetMaxSize(size_t size) = 0;
   virtual bool Enable(bool enable) = 0;
   virtual bool IsEnabled() const = 0;
-  virtual void OnRtpPacket(const void* data, size_t size) = 0;
-  virtual void OnRtcpPacket(const void* data, size_t size) = 0;
+  virtual void OnPacket(const void* data, size_t size, bool rtcp) = 0;
+  virtual void set_packet_filter(int filter) = 0;
 };
 
 }  // namespace cricket
diff --git a/talk/session/phone/rtpdump.cc b/talk/session/phone/rtpdump.cc
index ad0e82d..6358399 100644
--- a/talk/session/phone/rtpdump.cc
+++ b/talk/session/phone/rtpdump.cc
@@ -28,6 +28,7 @@
 #include "talk/session/phone/rtpdump.h"
 
 #include <string>
+#include <ctype.h>
 
 #include "talk/base/bytebuffer.h"
 #include "talk/base/byteorder.h"
diff --git a/talk/site_scons/site_tools/talk_linux.py b/talk/site_scons/site_tools/talk_linux.py
new file mode 100644
index 0000000..1e204bc
--- /dev/null
+++ b/talk/site_scons/site_tools/talk_linux.py
@@ -0,0 +1,266 @@
+# Copyright 2010 Google Inc.
+# All Rights Reserved.
+# Author: tschmelcher@google.com (Tristan Schmelcher)
+
+"""Tool for helpers used in linux building process."""
+
+import os
+import SCons.Defaults
+import subprocess
+
+
+def _OutputFromShellCommand(command):
+  process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
+  return process.communicate()[0].strip()
+
+
+# This is a pure SCons helper function.
+def _InternalBuildDebianPackage(env, debian_files, package_files,
+    output_dir=None, force_version=None):
+  """Creates build rules to build a Debian package from the specified sources.
+
+  Args:
+    env: SCons Environment.
+    debian_files: Array of the Debian control file sources that should be
+        copied into the package source tree, e.g., changelog, control, rules,
+        etc.
+    package_files: An array of 2-tuples listing the files that should be
+        copied into the package source tree.
+        The first element is the path where the file should be placed for the
+            .install control file to find it, relative to the generated debian
+            package source directory.
+        The second element is the file source.
+    output_dir: An optional directory to place the files in. If omitted, the
+        current output directory is used.
+    force_version: Optional. Forces the version of the package to start with
+        this version string if specified. If the last entry in the changelog
+        is not for a version that starts with this then a dummy entry is
+        generated with this version and a ~prerelease suffix (so that the
+        final version will compare as greater).
+
+  Return:
+    A list of the targets (if any).
+  """
+  if 0 != subprocess.call(['which', 'dpkg-buildpackage']):
+    print ('dpkg-buildpackage not installed on this system; '
+           'skipping DEB build stage')
+    return []
+  # Read the control file and changelog file to determine the package name,
+  # version, and arch that the Debian build tools will use to name the
+  # generated files.
+  control_file = None
+  changelog_file = None
+  for file in debian_files:
+    if os.path.basename(file) == 'control':
+      control_file = env.File(file).srcnode().abspath
+    elif os.path.basename(file) == 'changelog':
+      changelog_file = env.File(file).srcnode().abspath
+  if not control_file:
+    raise Exception('Need to have a control file')
+  if not changelog_file:
+    raise Exception('Need to have a changelog file')
+  source = _OutputFromShellCommand(
+      "awk '/^Source:/ { print $2; }' " + control_file)
+  packages = _OutputFromShellCommand(
+      "awk '/^Package:/ { print $2; }' " + control_file).split('\n')
+  version = _OutputFromShellCommand(
+      "sed -nr '1 { s/.*\\((.*)\\).*/\\1/; p }' " + changelog_file)
+  arch = _OutputFromShellCommand('dpkg --print-architecture')
+  add_dummy_changelog_entry = False
+  if force_version and not version.startswith(force_version):
+    print ('Warning: no entry in ' + changelog_file + ' for version ' +
+        force_version + ' (last is ' + version +'). A dummy entry will be ' +
+        'generated. Remember to add the real changelog entry before ' +
+        'releasing.')
+    version = force_version + '~prerelease'
+    add_dummy_changelog_entry = True
+  source_dir_name = source + '_' + version + '_' + arch
+  target_file_names = [ source_dir_name + '.changes' ]
+  for package in packages:
+    package_file_name = package + '_' + version + '_' + arch + '.deb'
+    target_file_names.append(package_file_name)
+  # The targets
+  if output_dir:
+    targets = [os.path.join(output_dir, s) for s in target_file_names]
+  else:
+    targets = target_file_names
+  # Path to where we will construct the debian build tree.
+  deb_build_tree = os.path.join(source_dir_name, 'deb_build_tree')
+  # First copy the files.
+  for file in package_files:
+    env.Command(os.path.join(deb_build_tree, file[0]), file[1],
+        SCons.Defaults.Copy('$TARGET', '$SOURCE'))
+    env.Depends(targets, os.path.join(deb_build_tree, file[0]))
+  # Now copy the Debian metadata sources. We have to do this all at once so
+  # that we can remove the target directory before copying, because there
+  # can't be any other stale files there or else dpkg-buildpackage may use
+  # them and give incorrect build output.
+  copied_debian_files_paths = []
+  for file in debian_files:
+    copied_debian_files_paths.append(os.path.join(deb_build_tree, 'debian',
+        os.path.basename(file)))
+  copy_commands = [
+      """dir=$$(dirname $TARGET) && \
+          rm -Rf $$dir && \
+          mkdir -p $$dir && \
+          cp $SOURCES $$dir && \
+          chmod -R u+w $$dir"""
+  ]
+  if add_dummy_changelog_entry:
+    copy_commands += [
+        """debchange -c $$(dirname $TARGET)/changelog --newversion %s \
+            --distribution UNRELEASED \
+            'Developer preview build. (This entry was auto-generated.)'""" %
+        version
+    ]
+  env.Command(copied_debian_files_paths, debian_files, copy_commands)
+  env.Depends(targets, copied_debian_files_paths)
+  # Must explicitly specify -a because otherwise cross-builds won't work.
+  # Must explicitly specify -D because -a disables it.
+  # Must explicitly specify fakeroot because old dpkg tools don't assume that.
+  env.Command(targets, None,
+      """dir=%(dir)s && \
+          cd $$dir && \
+          dpkg-buildpackage -b -uc -a%(arch)s -D -rfakeroot && \
+          cd $$OLDPWD && \
+          for file in %(targets)s; do \
+            mv $$dir/../$$file $$(dirname $TARGET) || exit 1; \
+          done""" %
+      {'dir':env.Dir(deb_build_tree).path,
+       'arch':arch,
+       'targets':' '.join(target_file_names)})
+  return targets
+
+
+def BuildDebianPackage(env, debian_files, package_files, force_version=None):
+  """Creates build rules to build a Debian package from the specified sources.
+
+  This is a Hammer-ified version of _InternalBuildDebianPackage that knows to
+  put the packages in the Hammer staging dir.
+
+  Args:
+    env: SCons Environment.
+    debian_files: Array of the Debian control file sources that should be
+        copied into the package source tree, e.g., changelog, control, rules,
+        etc.
+    package_files: An array of 2-tuples listing the files that should be
+        copied into the package source tree.
+        The first element is the path where the file should be placed for the
+            .install control file to find it, relative to the generated debian
+            package source directory.
+        The second element is the file source.
+    force_version: Optional. Forces the version of the package to start with
+        this version string if specified. If the last entry in the changelog
+        is not for a version that starts with this then a dummy entry is
+        generated with this version and a ~prerelease suffix (so that the
+        final version will compare as greater).
+
+  Return:
+    A list of the targets (if any).
+  """
+  if not env.Bit('host_linux'):
+    return []
+  return _InternalBuildDebianPackage(env, debian_files, package_files,
+      output_dir='$STAGING_DIR', force_version=force_version)
+
+
+def _HavePackage(package):
+  """Whether the given pkg-config package name is present on the build system.
+
+  Args:
+    package: The name of the package.
+
+  Returns:
+    True if the package is present, else False
+  """
+  return subprocess.call(['pkg-config', '--exists', package]) == 0
+
+
+def _GetPackageFlags(flag_type, packages):
+  """Get the flags needed to compile/link against the given package(s).
+
+  Returns the flags that are needed to compile/link against the given pkg-config
+  package(s).
+
+  Args:
+    flag_type: The option to pkg-config specifying the type of flags to get.
+    packages: The list of package names as strings.
+
+  Returns:
+    The flags of the requested type.
+  """
+  process = subprocess.Popen(['pkg-config', flag_type] + packages,
+                             stdout=subprocess.PIPE)
+  return process.communicate()[0].strip().split(' ')
+
+
+def GetPackageParams(env, packages):
+  """Get the params needed to compile/link against the given package(s).
+
+  Returns the params that are needed to compile/link against the given
+  pkg-config package(s).
+
+  Args:
+    env: The current SCons environment.
+    packages: The name of the package, or a list of names.
+
+  Returns:
+    A dictionary containing the params.
+
+  Raises:
+    Exception: One or more of the packages is not installed.
+  """
+  if not env.Bit('host_linux'):
+    return {}
+  if not SCons.Util.is_List(packages):
+    packages = [packages]
+  for package in packages:
+    if not _HavePackage(package):
+      raise Exception(('Required package \"%s\" was not found. Please install '
+                       'the package that provides the \"%s.pc\" file.') %
+                      (package, package))
+  package_ccflags = _GetPackageFlags('--cflags', packages)
+  package_libs = _GetPackageFlags('--libs', packages)
+  # Split package_libs into actual libs and non-lib linker flags.
+  libs = [flag[2:] for flag in package_libs if flag[0:2] == '-l']
+  link_flags = [flag for flag in package_libs if flag[0:2] != '-l']
+  return {
+      'ccflags': package_ccflags,
+      'libs': libs,
+      'link_flags': link_flags,
+      'dependent_target_settings' : {
+          'libs': libs[:],
+          'link_flags': link_flags[:],
+      },
+  }
+
+
+def EnableFeatureWherePackagePresent(env, bit, cpp_flag, package):
+  """Enable a feature if a required pkg-config package is present.
+
+  Args:
+    env: The current SCons environment.
+    bit: The name of the Bit to enable when the package is present.
+    cpp_flag: The CPP flag to enable when the package is present.
+    package: The name of the package.
+  """
+  if not env.Bit('host_linux'):
+    return
+  if _HavePackage(package):
+    env.SetBits(bit)
+    env.Append(CPPDEFINES=[cpp_flag])
+  else:
+    print ('Warning: Package \"%s\" not found. Feature \"%s\" will not be '
+           'built. To build with this feature, install the package that '
+           'provides the \"%s.pc\" file.') % (package, bit, package)
+
+
+def generate(env):
+  if env.Bit('linux'):
+    env.AddMethod(EnableFeatureWherePackagePresent)
+    env.AddMethod(GetPackageParams)
+    env.AddMethod(BuildDebianPackage)
+
+
+def exists(env):
+  return 1  # Required by scons
diff --git a/talk/site_scons/talk.py b/talk/site_scons/talk.py
index 94f1b14..e2a2166 100644
--- a/talk/site_scons/talk.py
+++ b/talk/site_scons/talk.py
@@ -5,10 +5,108 @@
 #         Daniel Petersson (dape@google.com)
 #
 import os
+import SCons.Util
 
 # Keep a global dictionary of library target params for lookups in
 # ExtendComponent().
 _all_lib_targets = {}
+# Maintain a set of all prebuilt static libraries.
+_all_prebuilt_libraries = set()
+# Set of libraries not found in the above (used to detect out-of-order build
+# rules).
+_all_system_libraries = set()
+
+def _GetLibParams(lib):
+  """Gets the params for the given library if it is a library target.
+
+  Returns the params that were specified when the given lib target name was
+  created, or None if no such lib target has been defined. In the None case, it
+  additionally records the negative result so as to detect out-of-order
+  dependencies for future targets.
+
+  Args:
+    lib: The library's name as a string.
+
+  Returns:
+    Its dictionary of params, or None.
+  """
+  if lib in _all_lib_targets:
+    return _all_lib_targets[lib]
+  else:
+    if lib not in _all_prebuilt_libraries and lib not in _all_system_libraries:
+      _all_system_libraries.add(lib)
+    return None
+
+
+def _RecordLibParams(lib, params):
+  """Record the params used for a library target.
+
+  Record the params used for a library target while checking for several error
+  conditions.
+
+  Args:
+    lib: The library target's name as a string.
+    params: Its dictionary of params.
+
+  Raises:
+    Exception: The lib target has already been recorded, or the lib was
+        previously declared to be prebuilt, or the lib target is being defined
+        after a reverse library dependency.
+  """
+  if lib in _all_lib_targets:
+    raise Exception('Multiple definitions of ' + lib)
+  if lib in _all_prebuilt_libraries:
+    raise Exception(lib + ' already declared as a prebuilt library')
+  if lib in _all_system_libraries:
+    raise Exception(lib + ' cannot be defined after its reverse library '
+                    'dependencies')
+  _all_lib_targets[lib] = params
+
+
+def _IsPrebuiltLibrary(lib):
+  """Checks whether or not the given library is a prebuilt static library.
+
+  Returns whether or not the given library name has been declared to be a
+  prebuilt static library. In the False case, it additionally records the
+  negative result so as to detect out-of-order dependencies for future targets.
+
+  Args:
+    lib: The library's name as a string.
+
+  Returns:
+    True or False
+  """
+  if lib in _all_prebuilt_libraries:
+    return True
+  else:
+    if lib not in _all_lib_targets and lib not in _all_system_libraries:
+      _all_system_libraries.add(lib)
+    return False
+
+
+def _RecordPrebuiltLibrary(lib):
+  """Record that a library is a prebuilt static library.
+
+  Record that the given library name refers to a prebuilt static library while
+  checking for several error conditions.
+
+  Args:
+    lib: The library's name as a string.
+
+  Raises:
+    Exception: The lib has already been recorded to be prebuilt, or the lib was
+        previously declared as a target, or the lib is being declared as
+        prebuilt after a reverse library dependency.
+  """
+  if lib in _all_prebuilt_libraries:
+    raise Exception('Multiple prebuilt declarations of ' + lib)
+  if lib in _all_lib_targets:
+    raise Exception(lib + ' already defined as a target')
+  if lib in _all_system_libraries:
+    raise Exception(lib + ' cannot be declared as prebuilt after its reverse '
+                    'library dependencies')
+  _all_prebuilt_libraries.add(lib)
+
 
 def _GenericLibrary(env, static, **kwargs):
   """Extends ComponentLibrary to support multiplatform builds
@@ -25,6 +123,22 @@
   return ExtendComponent(env, 'ComponentLibrary', **params)
 
 
+def DeclarePrebuiltLibraries(libraries):
+  """Informs the build engine about external static libraries.
+
+  Informs the build engine that the given external library name(s) are prebuilt
+  static libraries, as opposed to shared libraries.
+
+  Args:
+    libraries: The library or libraries that are being declared as prebuilt
+        static libraries.
+  """
+  if not SCons.Util.is_List(libraries):
+    libraries = [libraries]
+  for library in libraries:
+    _RecordPrebuiltLibrary(library)
+
+
 def Library(env, **kwargs):
   """Extends ComponentLibrary to support multiplatform builds of static
      libraries.
@@ -74,7 +188,7 @@
     'posix_cppdefines': ['GUNIT_NO_GOOGLE3', 'GTEST_HAS_RTTI=0'],
     'libs': ['unittest_main', 'gunit']
   }
-  if not kwargs.has_key('explicit_libs'):
+  if 'explicit_libs' not in kwargs:
     common_test_params['win_libs'] = [
       'advapi32',
       'crypt32',
@@ -107,7 +221,7 @@
   Returns:
     See swtoolkit ComponentProgram.
   """
-  if not kwargs.has_key('explicit_libs'):
+  if 'explicit_libs' not in kwargs:
     common_app_params = {
       'win_libs': [
         'advapi32',
@@ -184,96 +298,11 @@
   return '%s/%s.scons' % (path, os.path.basename(path))
 
 
-def AddMediaLibs(env, **kwargs):
-  lmi_libdir = '$GOOGLE3/../googleclient/third_party/lmi/files/lib/'
-  if env.Bit('windows'):
-    if env.get('COVERAGE_ENABLED'):
-      lmi_libdir += 'win32/c_only'
-    else:
-      lmi_libdir += 'win32/Release'
-  elif env.Bit('mac'):
-    lmi_libdir += 'macos'
-  elif env.Bit('linux'):
-      lmi_libdir += 'linux/x86'
-
-
-  AddToDict(kwargs, 'libdirs', [
-    '$MAIN_DIR/third_party/gips/Libraries/',
-    lmi_libdir,
-  ])
-
-  gips_lib = ''
-  if env.Bit('windows'):
-    if env.Bit('debug'):
-      gips_lib = 'gipsvoiceenginelib_mtd'
-    else:
-      gips_lib = 'gipsvoiceenginelib_mt'
-  elif env.Bit('mac'):
-    gips_lib = 'VoiceEngine_mac_universal_gcc'
-  elif env.Bit('linux'):
-      gips_lib = 'VoiceEngine_Linux_gcc'
-
-
-  AddToDict(kwargs, 'libs', [
-    gips_lib,
-    'LmiAudioCommon',
-    'LmiClient',
-    'LmiCmcp',
-    'LmiDeviceManager',
-    'LmiH263ClientPlugIn',
-    'LmiH263CodecCommon',
-    'LmiH263Decoder',
-    'LmiH263Encoder',
-    'LmiH264ClientPlugIn',
-    'LmiH264CodecCommon',
-    'LmiH264Common',
-    'LmiH264Decoder',
-    'LmiH264Encoder',
-    'LmiIce',
-    'LmiMediaPayload',
-    'LmiOs',
-    'LmiPacketCache',
-    'LmiProtocolStack',
-    'LmiRateShaper',
-    'LmiRtp',
-    'LmiSecurity',
-    'LmiSignaling',
-    'LmiStun',
-    'LmiTransport',
-    'LmiUi',
-    'LmiUtils',
-    'LmiVideoCommon',
-    'LmiXml',
-  ])
-
-  if env.Bit('windows'):
-    AddToDict(kwargs, 'libs', [
-      'dsound',
-      'd3d9',
-      'gdi32',
-      'strmiids',
-    ])
-
-  if env.Bit('mac'):
-    AddToDict(kwargs, 'FRAMEWORKS', [
-      'AudioToolbox',
-      'AudioUnit',
-      'Cocoa',
-      'CoreAudio',
-      'CoreFoundation',
-      'IOKit',
-      'QTKit',
-      'QuickTime',
-      'QuartzCore',
-    ])
-  return kwargs
-
-
 def ReadVersion(filename):
   """Executes the supplied file and pulls out a version definition from it. """
   defs = {}
   execfile(str(filename), defs)
-  if not defs.has_key('version'):
+  if 'version' not in defs:
     return '0.0.0.0'
   version = defs['version']
   parts = version.split(',')
@@ -287,23 +316,22 @@
 # Helper methods for translating talk.Foo() declarations in to manipulations of
 # environmuent construction variables, including parameter parsing and merging,
 #
-def GetEntry(dict, key):
+def PopEntry(dictionary, key):
   """Get the value from a dictionary by key. If the key
      isn't in the dictionary then None is returned. If it is in
-     the dictionaruy the value is fetched and then is it removed
+     the dictionary the value is fetched and then is it removed
      from the dictionary.
 
   Args:
+    dictionary: The dictionary.
     key: The key to get the value for.
-    kwargs: The keyword argument dictionary.
   Returns:
     The value or None if the key is missing.
   """
   value = None
-  if dict.has_key(key):
-    value = dict[key]
-    dict.pop(key)
-
+  if key in dictionary:
+    value = dictionary[key]
+    dictionary.pop(key)
   return value
 
 
@@ -355,6 +383,7 @@
 
   return merged
 
+
 # Linux can build both 32 and 64 bit on 64 bit host, but 32 bit host can
 # only build 32 bit.  For 32 bit debian installer a 32 bit host is required.
 # ChromeOS (linux) ebuild don't support 64 bit and requires 32 bit build only
@@ -363,18 +392,21 @@
   return (env.Bit('linux') and env.Bit('platform_arch_64bit')
           )
 
+
 def MergeSettingsFromLibraryDependencies(env, params):
-  if params.has_key('libs'):
+  if 'libs' in params:
     for lib in params['libs']:
-      if (_all_lib_targets.has_key(lib) and
-          _all_lib_targets[lib].has_key('dependent_target_settings')):
-        params = CombineDicts(
-            params,
-            MergeAndFilterByPlatform(
-                env,
-                _all_lib_targets[lib]['dependent_target_settings']))
+      libparams = _GetLibParams(lib)
+      if libparams:
+        if 'dependent_target_settings' in libparams:
+          params = CombineDicts(
+              params,
+              MergeAndFilterByPlatform(
+                  env,
+                  libparams['dependent_target_settings']))
   return params
 
+
 def ExtendComponent(env, component, **kwargs):
   """A wrapper around a scons builder function that preprocesses and post-
      processes its inputs and outputs.  For example, it merges and filters
@@ -399,31 +431,32 @@
   params = MergeAndFilterByPlatform(env, kwargs)
 
   # get the 'target' field
-  name = GetEntry(params, 'name')
+  name = PopEntry(params, 'name')
+
+  # get the 'packages' field and process it if present (only used for Linux).
+  packages = PopEntry(params, 'packages')
+  if packages and len(packages):
+    params = CombineDicts(params, env.GetPackageParams(packages))
 
   # save pristine params of lib targets for future reference
   if 'ComponentLibrary' == component:
-    _all_lib_targets[name] = dict(params)
+    _RecordLibParams(name, dict(params))
 
   # add any dependent target settings from library dependencies
   params = MergeSettingsFromLibraryDependencies(env, params)
 
   # if this is a signed binary we need to make an unsigned version first
-  signed = env.Bit('windows') and GetEntry(params, 'signed')
+  signed = env.Bit('windows') and PopEntry(params, 'signed')
   if signed:
     name = 'unsigned_' + name
 
-  # add default values
-  if GetEntry(params, 'include_talk_media_libs'):
-    params = AddMediaLibs(env, **params)
-
   # potentially exit now
-  srcs = GetEntry(params, 'srcs')
+  srcs = PopEntry(params, 'srcs')
   if not srcs or not hasattr(env, component):
     return None
 
   # apply any explicit dependencies
-  dependencies = GetEntry(params, 'depends')
+  dependencies = PopEntry(params, 'depends')
   if dependencies is not None:
     env.Depends(name, dependencies)
 
@@ -443,17 +476,17 @@
   else:
     # ... while GCC compile flags have precedence at the end
     appends['ccflags'] = 'CCFLAGS'
-  if GetEntry(params, 'prepend_includedirs'):
+  if PopEntry(params, 'prepend_includedirs'):
     prepends['includedirs'] = 'CPPPATH'
   else:
     appends['includedirs'] = 'CPPPATH'
 
   for field, var in appends.items():
-    values = GetEntry(params, field)
+    values = PopEntry(params, field)
     if values is not None:
       env.Append(**{var : values})
   for field, var in prepends.items():
-    values = GetEntry(params, field)
+    values = PopEntry(params, field)
     if values is not None:
       env.Prepend(**{var : values})
 
@@ -468,13 +501,30 @@
   for field, value in params.items():
     env.Replace(**{field : value})
 
+  if env.Bit('linux') and 'LIBS' in env:
+    libs = env['LIBS']
+    # When using --as-needed + --start/end-group, shared libraries need to come
+    # after --end-group on the command-line because the pruning decision only
+    # considers the preceding modules and --start/end-group may cause the
+    # effective position of early static libraries on the command-line to be
+    # deferred to the point of --end-group. To effect this, we move shared libs
+    # into _LIBFLAGS, which has the --end-group as its first entry. SCons does
+    # not track dependencies on system shared libraries anyway so we lose
+    # nothing by removing them from LIBS.
+    static_libs = [lib for lib in libs if
+                   _GetLibParams(lib) or _IsPrebuiltLibrary(lib)]
+    shared_libs = ['-l' + lib for lib in libs if not
+                   (_GetLibParams(lib) or _IsPrebuiltLibrary(lib))]
+    env.Replace(LIBS=static_libs)
+    env.Append(_LIBFLAGS=shared_libs)
+
   # invoke the builder function
   builder = getattr(env, component)
 
   node = builder(name, srcs)
 
   # make a parallel 64bit version if requested
-  if Allow64BitCompile(env) and GetEntry(params, 'also64bit'):
+  if Allow64BitCompile(env) and PopEntry(params, 'also64bit'):
     env_64bit = env.Clone()
     env_64bit.FilterOut(CCFLAGS = ['-m32'], LINKFLAGS = ['-m32'])
     env_64bit.Prepend(CCFLAGS = ['-m64', '-fPIC'], LINKFLAGS = ['-m64'])
@@ -487,8 +537,8 @@
       # link 64 bit versions of libraries
       libs = []
       for lib in env_64bit['LIBS']:
-        if (_all_lib_targets.has_key(lib) and
-            _all_lib_targets[lib].has_key('also64bit')):
+        libparams = _GetLibParams(lib)
+        if libparams and 'also64bit' in libparams:
           libs.append(lib + '64')
         else:
           libs.append(lib)
@@ -513,7 +563,7 @@
     # same name.  Setting postsignprefix allows the EXE and its PDB
     # to be renamed and copied in a previous step; then the desired
     # name of the EXE (but not PDB) is reconstructed after signing.
-    postsignprefix = GetEntry(params, 'postsignprefix')
+    postsignprefix = PopEntry(params, 'postsignprefix')
     if postsignprefix is not None:
         target = postsignprefix + target
     signed_node = env.SignedBinary(
@@ -534,7 +584,7 @@
   if values is None:
     return
 
-  if not dictionary.has_key(key):
+  if key not in dictionary:
     dictionary[key] = values
     return
 
@@ -551,12 +601,28 @@
 
 
 def CombineDicts(a, b):
-  """Unions two dictionaries by combining values of keys shared between them.
+  """Unions two dictionaries of arrays/dictionaries.
+
+  Unions two dictionaries of arrays/dictionaries by combining the values of keys
+  shared between them. The original dictionaries should not be used again after
+  this call.
+
+  Args:
+    a: First dict.
+    b: Second dict.
+
+  Returns:
+    The union of a and b.
   """
   c = {}
   for key in a:
-    if b.has_key(key):
-      c[key] = a[key] + b.pop(key)
+    if key in b:
+      aval = a[key]
+      bval = b.pop(key)
+      if isinstance(aval, dict) and isinstance(bval, dict):
+        c[key] = CombineDicts(aval, bval)
+      else:
+        c[key] = aval + bval
     else:
       c[key] = a[key]
 
@@ -567,4 +633,4 @@
 
 
 def RenameKey(d, old, new, append=True):
-  AddToDict(d, new, GetEntry(d, old), append)
+  AddToDict(d, new, PopEntry(d, old), append)
diff --git a/talk/xmllite/qname.h b/talk/xmllite/qname.h
index 3e64726..172a067 100644
--- a/talk/xmllite/qname.h
+++ b/talk/xmllite/qname.h
@@ -29,6 +29,7 @@
 #define _qname_h_
 
 #include <string>
+#include "talk/base/criticalsection.h"
 
 namespace buzz {
 
@@ -49,7 +50,7 @@
     return *this;
   }
   ~QName();
-  
+
   const std::string & Namespace() const { return data_->namespace_; }
   const std::string & LocalPart() const { return data_->localPart_; }
   std::string Merged() const;
@@ -57,7 +58,7 @@
   bool operator==(const QName & other) const;
   bool operator!=(const QName & other) const { return !operator==(other); }
   bool operator<(const QName & other) const { return Compare(other) < 0; }
-  
+
   class Data {
   public:
     Data(const std::string & ns, const std::string & local) :
@@ -66,11 +67,11 @@
       refcount_(1) {}
 
     Data() : refcount_(0) {}
-      
+
     std::string namespace_;
     std::string localPart_;
-    void AddRef() { refcount_++; }
-    void Release() { if (!--refcount_) { delete this; } }
+    void AddRef() { talk_base::AtomicOps::Increment(&refcount_); }
+    void Release() { if (!talk_base::AtomicOps::Decrement(&refcount_)) { delete this; } }
     bool Occupied() { return !!refcount_; }
 
   private: