Update in preparation for libjingle 0.5.7:

- Support for setting MUC display name
- Update STUN support to RFC5389
- Handle description-info message

git-svn-id: http://libjingle.googlecode.com/svn/trunk@67 dd674b97-3498-5ee5-1854-bdd07cd0ff33
diff --git a/README b/README
index 9867fbf..ccf22ea 100644
--- a/README
+++ b/README
@@ -94,6 +94,7 @@
   * Third, go to the talk/ directory and run $path_to_swtoolkit/hammer.sh. Run
     $path_to_swtoolkit/hammer.sh --help for information on how to build for
     different modes.
+  * On Linux, you need to install libssl-dev, libasound2-dev and gtk+2.0.
 
 2.3 Build Libjingle under Windows
   * First, make sure the SCONS_DIR environment variable is set correctly and
@@ -141,6 +142,6 @@
     In the "talk.App(env, name = "call",..." section, you need to add:
       "mediastreamer",
     to the "libs = [".
-  * Add the following line into the "srcs = [ ..." section of the
-    libjingle.scons file.
+  * In the libjingle.scons file, add the following line into the "srcs = [ ..."
+    section of the "libjingle" Library.
       "session/phone/linphonemediaengine.cc",
diff --git a/talk/base/diskcache.cc b/talk/base/diskcache.cc
index 4e70543..afaf9d2 100644
--- a/talk/base/diskcache.cc
+++ b/talk/base/diskcache.cc
@@ -139,7 +139,7 @@
   }
 
   scoped_ptr<FileStream> file(new FileStream);
-  if (!file->Open(filename, "wb")) {
+  if (!file->Open(filename, "wb", NULL)) {
     LOG_F(LS_ERROR) << "Couldn't create cache file";
     return NULL;
   }
@@ -177,7 +177,7 @@
     return NULL;
 
   scoped_ptr<FileStream> file(new FileStream);
-  if (!file->Open(IdToFilename(id, index), "rb"))
+  if (!file->Open(IdToFilename(id, index), "rb", NULL))
     return NULL;
 
   entry->accessors += 1;
diff --git a/talk/base/fileutils.h b/talk/base/fileutils.h
index 217cd83..644516d 100644
--- a/talk/base/fileutils.h
+++ b/talk/base/fileutils.h
@@ -118,6 +118,8 @@
 
   // Opens a file. Returns an open StreamInterface if function succeeds.
   // Otherwise, returns NULL.
+  // TODO: Add an error param to indicate failure reason, similar to
+  // FileStream::Open
   virtual FileStream *OpenFile(const Pathname &filename,
                                const std::string &mode) = 0;
 
diff --git a/talk/base/helpers.cc b/talk/base/helpers.cc
index f8d8a9f..e2dabde 100644
--- a/talk/base/helpers.cc
+++ b/talk/base/helpers.cc
@@ -224,7 +224,9 @@
   return str;
 }
 
-bool CreateRandomString(size_t len, std::string* str) {
+bool CreateRandomString(size_t len,
+                        const char* table, int table_size,
+                        std::string* str) {
   str->clear();
   scoped_array<uint8> bytes(new uint8[len]);
   if (!g_rng->Generate(bytes.get(), len)) {
@@ -233,11 +235,20 @@
   }
   str->reserve(len);
   for (size_t i = 0; i < len; ++i) {
-    str->push_back(BASE64[bytes[i] & 63]);
+    str->push_back(table[bytes[i] % table_size]);
   }
   return true;
 }
 
+bool CreateRandomString(size_t len, std::string* str) {
+  return CreateRandomString(len, BASE64, 64, str);
+}
+
+bool CreateRandomString(size_t len, const std::string& table,
+                        std::string* str) {
+  return CreateRandomString(len, table.c_str(), table.size(), str);
+}
+
 uint32 CreateRandomId() {
   uint32 id;
   if (!g_rng->Generate(&id, sizeof(id))) {
diff --git a/talk/base/helpers.h b/talk/base/helpers.h
index 83a3c7f..74867c0 100644
--- a/talk/base/helpers.h
+++ b/talk/base/helpers.h
@@ -50,6 +50,12 @@
 // Return false if the random number generator failed.
 bool CreateRandomString(size_t length, std::string* str);
 
+// Generates a (cryptographically) random string of the given length,
+// with characters from the given table. Return false if the random
+// number generator failed.
+bool CreateRandomString(size_t length, const std::string& table,
+                        std::string* str);
+
 // Generates a random id.
 uint32 CreateRandomId();
 
diff --git a/talk/base/httpcommon.cc b/talk/base/httpcommon.cc
index f7553b2..229aef9 100644
--- a/talk/base/httpcommon.cc
+++ b/talk/base/httpcommon.cc
@@ -390,7 +390,7 @@
     int hours = (zone[1] - '0') * 10 + (zone[2] - '0');
     int minutes = (zone[3] - '0') * 10 + (zone[4] - '0');
     int offset = (hours * 60 + minutes) * 60;
-    gmt = non_gmt + (zone[0] == '+') ? offset : -offset;
+    gmt = non_gmt + ((zone[0] == '+') ? offset : -offset);
   } else {
     size_t zindex;
     if (!find_string(zindex, zone, kTimeZones, ARRAY_SIZE(kTimeZones))) {
diff --git a/talk/base/linux.cc b/talk/base/linux.cc
index 514ab94..ba80ff5 100644
--- a/talk/base/linux.cc
+++ b/talk/base/linux.cc
@@ -176,7 +176,7 @@
 
 bool ConfigParser::Open(const std::string& filename) {
   FileStream* fs = new FileStream();
-  if (!fs->Open(filename, "r")) {
+  if (!fs->Open(filename, "r", NULL)) {
     return false;
   }
   instream_.reset(fs);
@@ -335,7 +335,7 @@
   FileStream fs;
   std::string str;
   int freq = -1;
-  if (!fs.Open(kCpuMaxFreqFile, "r") ||
+  if (!fs.Open(kCpuMaxFreqFile, "r", NULL) ||
       SR_SUCCESS != fs.ReadLine(&str) ||
       !FromString(str, &freq)) {
     return -1;
diff --git a/talk/base/logging.cc b/talk/base/logging.cc
index 654d8c6..b5c588c 100644
--- a/talk/base/logging.cc
+++ b/talk/base/logging.cc
@@ -333,7 +333,7 @@
   scoped_ptr<FileStream> stream;
   if (NO_LOGGING != file_level) {
     stream.reset(new FileStream);
-    if (!stream->Open(filename, "wb") || !stream->DisableBuffering()) {
+    if (!stream->Open(filename, "wb", NULL) || !stream->DisableBuffering()) {
       stream.reset();
     }
   }
diff --git a/talk/base/network.cc b/talk/base/network.cc
index 8a56d0a..e5dffc1 100644
--- a/talk/base/network.cc
+++ b/talk/base/network.cc
@@ -208,7 +208,7 @@
 // Gets the default gateway for the specified interface.
 uint32 GetDefaultGateway(const std::string& name) {
 #ifdef OSX
-  // TODO: /proc/net/route doesn't exist, 
+  // TODO: /proc/net/route doesn't exist,
   // Use ioctl to get the routing table
   return 0xFFFFFFFF;
 #endif
@@ -216,7 +216,7 @@
   uint32 gateway_ip = 0;
 
   FileStream fs;
-  if (fs.Open("/proc/net/route", "r")) {
+  if (fs.Open("/proc/net/route", "r", NULL)) {
     std::string line;
     while (fs.ReadLine(&line) == SR_SUCCESS && gateway_ip == 0) {
       char iface[16];
diff --git a/talk/base/stream.cc b/talk/base/stream.cc
index 3022d3e..cdd6098 100644
--- a/talk/base/stream.cc
+++ b/talk/base/stream.cc
@@ -349,35 +349,49 @@
   FileStream::Close();
 }
 
-bool FileStream::Open(const std::string& filename, const char* mode) {
+bool FileStream::Open(const std::string& filename, const char* mode,
+                      int* error) {
   Close();
 #ifdef WIN32
   std::wstring wfilename;
   if (Utf8ToWindowsFilename(filename, &wfilename)) {
     file_ = _wfopen(wfilename.c_str(), ToUtf16(mode).c_str());
   } else {
-    file_ = NULL;
+    if (error) {
+      *error = -1;
+      return false;
+    }
   }
 #else
   file_ = fopen(filename.c_str(), mode);
 #endif
+  if (!file_ && error) {
+    *error = errno;
+  }
   return (file_ != NULL);
 }
 
 bool FileStream::OpenShare(const std::string& filename, const char* mode,
-                           int shflag) {
+                           int shflag, int* error) {
   Close();
 #ifdef WIN32
   std::wstring wfilename;
   if (Utf8ToWindowsFilename(filename, &wfilename)) {
     file_ = _wfsopen(wfilename.c_str(), ToUtf16(mode).c_str(), shflag);
+    if (!file_ && error) {
+      *error = errno;
+      return false;
+    }
+    return file_ != NULL;
   } else {
-    file_ = NULL;
+    if (error) {
+      *error = -1;
+    }
+    return false;
   }
 #else
-  return Open(filename, mode);
+  return Open(filename, mode, error);
 #endif
-  return (file_ != NULL);
 }
 
 bool FileStream::DisableBuffering() {
diff --git a/talk/base/stream.h b/talk/base/stream.h
index a8d399d..496b4ce 100644
--- a/talk/base/stream.h
+++ b/talk/base/stream.h
@@ -410,9 +410,9 @@
   virtual ~FileStream();
 
   // The semantics of filename and mode are the same as stdio's fopen
-  virtual bool Open(const std::string& filename, const char* mode);
+  virtual bool Open(const std::string& filename, const char* mode, int* error);
   virtual bool OpenShare(const std::string& filename, const char* mode,
-                         int shflag);
+                         int shflag, int* error);
 
   // By default, reads and writes are buffered for efficiency.  Disabling
   // buffering causes writes to block until the bytes on disk are updated.
diff --git a/talk/base/thread.h b/talk/base/thread.h
index 8eeef42..42656e9 100644
--- a/talk/base/thread.h
+++ b/talk/base/thread.h
@@ -48,7 +48,7 @@
 class Thread;
 
 class ThreadManager {
-public:
+ public:
   ThreadManager();
   ~ThreadManager();
 
@@ -75,7 +75,7 @@
 
   static void StopAllThreads_();  // Experimental
 
-private:
+ private:
   Thread *main_thread_;
   std::vector<Thread *> threads_;
   CriticalSection crit_;
@@ -112,7 +112,7 @@
 };
 
 class Thread : public MessageQueue {
-public:
+ public:
   Thread(SocketServer* ss = NULL);
   virtual ~Thread();
 
@@ -184,11 +184,13 @@
   }
 #endif
 
-private:
-  static void *PreRun(void *pv);
+ protected:
   // Blocks the calling thread until this thread has terminated.
   void Join();
 
+ private:
+  static void *PreRun(void *pv);
+
   std::list<_SendMessage> sendlist_;
   std::string name_;
   ThreadPriority priority_;
diff --git a/talk/base/unixfilesystem.cc b/talk/base/unixfilesystem.cc
index 436dc2f..2a243ba 100644
--- a/talk/base/unixfilesystem.cc
+++ b/talk/base/unixfilesystem.cc
@@ -124,7 +124,7 @@
 FileStream *UnixFilesystem::OpenFile(const Pathname &filename,
                                      const std::string &mode) {
   FileStream *fs = new FileStream();
-  if (fs && !fs->Open(filename.pathname().c_str(), mode.c_str())) {
+  if (fs && !fs->Open(filename.pathname().c_str(), mode.c_str(), NULL)) {
     delete fs;
     fs = NULL;
   }
diff --git a/talk/base/win32filesystem.cc b/talk/base/win32filesystem.cc
index ccf4618..42c0388 100644
--- a/talk/base/win32filesystem.cc
+++ b/talk/base/win32filesystem.cc
@@ -80,7 +80,7 @@
 FileStream *Win32Filesystem::OpenFile(const Pathname &filename,
                                       const std::string &mode) {
   FileStream *fs = new FileStream();
-  if (fs && !fs->Open(filename.pathname().c_str(), mode.c_str())) {
+  if (fs && !fs->Open(filename.pathname().c_str(), mode.c_str(), NULL)) {
     delete fs;
     fs = NULL;
   }
diff --git a/talk/examples/call/call_main.cc b/talk/examples/call/call_main.cc
index 9735cfc..8120f2e 100644
--- a/talk/examples/call/call_main.cc
+++ b/talk/examples/call/call_main.cc
@@ -200,6 +200,12 @@
   std::vector<cricket::AudioCodec> voice_codecs;
   voice_codecs.push_back(
       cricket::AudioCodec(9, "G722", 16000, 0, 1, 0));
+  voice_codecs.push_back(
+      cricket::AudioCodec(0, "PCMU", 8000, 0, 1, 0));
+  voice_codecs.push_back(
+      cricket::AudioCodec(13, "CN", 8000, 0, 1, 0));
+  voice_codecs.push_back(
+      cricket::AudioCodec(105, "CN", 16000, 0, 1, 0));    
   file_media_engine->set_voice_codecs(voice_codecs);
   std::vector<cricket::VideoCodec> video_codecs;
   video_codecs.push_back(
diff --git a/talk/examples/call/callclient.cc b/talk/examples/call/callclient.cc
index 3192daf..65bca1f 100644
--- a/talk/examples/call/callclient.cc
+++ b/talk/examples/call/callclient.cc
@@ -120,6 +120,7 @@
 "  ljoin [room_name]   Joins a MUC by looking up JID from room name.\n"
 "  invite user [room]  Invites a friend to a multi-user-chat.\n"
 "  leave [room]        Leaves a multi-user-chat.\n"
+"  nick [nick]         Sets the nick.\n"
 "  getdevs             Prints the available media devices.\n"
 "  quit                Quits the application.\n"
 "";
@@ -207,6 +208,8 @@
       InviteToMuc(words[1], GetWord(words, 2, ""));
     } else if (command == "leave") {
       LeaveMuc(GetWord(words, 1, ""));
+    } else if (command == "nick") {
+      SetNick(GetWord(words, 1, ""));
     } else if (command == "getdevs") {
       GetDevices();
     } else if ((words.size() == 2) && (command == "setvol")) {
@@ -638,6 +641,20 @@
   talk_base::Thread::Current()->Quit();
 }
 
+void CallClient::SetNick(const std::string& muc_nick) {
+  my_status_.set_nick(muc_nick);
+
+  // TODO: We might want to re-send presence, but right
+  // now, it appears to be ignored by the MUC.
+  //
+  // presence_out_->Send(my_status_); for (MucMap::const_iterator itr
+  // = mucs_.begin(); itr != mucs_.end(); ++itr) {
+  // presence_out_->SendDirected(itr->second->local_jid(),
+  // my_status_); }
+
+  console_->PrintLine("Nick set to '%s'.", muc_nick.c_str());
+}
+
 void CallClient::LookupAndJoinMuc(const std::string& room_name) {
   // The room_name can't be empty for lookup task.
   if (room_name.empty()) {
@@ -810,7 +827,10 @@
   mucs_.erase(elem);
 }
 
-void CallClient::InviteToMuc(const std::string& user, const std::string& room) {
+void CallClient::InviteToMuc(const std::string& given_user,
+                             const std::string& room) {
+  std::string user = given_user;
+
   // First find the room.
   const buzz::Muc* found_muc;
   if (room.length() == 0) {
@@ -828,19 +848,23 @@
     }
     found_muc = elem->second;
   }
+
+  buzz::Jid invite_to = found_muc->jid();
+
   // Now find the user. We invite all of their resources.
   bool found_user = false;
   buzz::Jid user_jid(user);
   for (RosterMap::iterator iter = roster_->begin();
        iter != roster_->end(); ++iter) {
     if (iter->second.jid.BareEquals(user_jid)) {
-      muc_invite_send_->Send(iter->second.jid, *found_muc);
+      buzz::Jid invitee = iter->second.jid;
+      muc_invite_send_->Send(invite_to, invitee);
       found_user = true;
     }
   }
   if (!found_user) {
-    console_->PrintLine("No such friend as %s.", user.c_str());
-    return;
+    buzz::Jid invitee = user_jid;
+    muc_invite_send_->Send(invite_to, invitee);
   }
 }
 
diff --git a/talk/examples/call/callclient.h b/talk/examples/call/callclient.h
index 72a9ca9..e39c9d4 100644
--- a/talk/examples/call/callclient.h
+++ b/talk/examples/call/callclient.h
@@ -118,6 +118,7 @@
   void LookupAndJoinMuc(const std::string& room_name);
   void InviteToMuc(const std::string& user, const std::string& room);
   void LeaveMuc(const std::string& room);
+  void SetNick(const std::string& muc_nick);
   void SetPortAllocatorFlags(uint32 flags) { portallocator_flags_ = flags; }
   void SetAllowLocalIps(bool allow_local_ips) {
     allow_local_ips_ = allow_local_ips;
diff --git a/talk/examples/call/mucinvitesendtask.cc b/talk/examples/call/mucinvitesendtask.cc
index efd9a81..d648fef 100644
--- a/talk/examples/call/mucinvitesendtask.cc
+++ b/talk/examples/call/mucinvitesendtask.cc
@@ -32,18 +32,18 @@
 namespace buzz {
 
 XmppReturnStatus
-MucInviteSendTask::Send(const Jid& user, const Muc& muc) {
+MucInviteSendTask::Send(const Jid& to, const Jid& invitee) {
   if (GetState() != STATE_INIT && GetState() != STATE_START)
     return XMPP_RETURN_BADSTATE;
 
   XmlElement* message = new XmlElement(QN_MESSAGE);
-  message->AddAttr(QN_TO, muc.jid().Str());
+  message->AddAttr(QN_TO, to.Str());
   XmlElement* xstanza = new XmlElement(QN_MUC_USER_X);
   XmlElement* invite = new XmlElement(QN_MUC_USER_INVITE);
-  invite->AddAttr(QN_TO, user.Str());
+  invite->AddAttr(QN_TO, invitee.Str());
   xstanza->AddElement(invite);
   message->AddElement(xstanza);
-  
+
   QueueStanza(message);
   return XMPP_RETURN_OK;
 }
diff --git a/talk/examples/call/mucinvitesendtask.h b/talk/examples/call/mucinvitesendtask.h
index 18e0f99..8afd361 100644
--- a/talk/examples/call/mucinvitesendtask.h
+++ b/talk/examples/call/mucinvitesendtask.h
@@ -39,7 +39,7 @@
   MucInviteSendTask(Task* parent) : XmppTask(parent) {}
   virtual ~MucInviteSendTask() {}
 
-  XmppReturnStatus Send(const Jid& user, const Muc& muc);
+  XmppReturnStatus Send(const Jid& to, const Jid& invitee);
 
   virtual int ProcessStart();
 };
diff --git a/talk/examples/call/presenceouttask.cc b/talk/examples/call/presenceouttask.cc
index b3e5f37..919a1af 100644
--- a/talk/examples/call/presenceouttask.cc
+++ b/talk/examples/call/presenceouttask.cc
@@ -106,6 +106,11 @@
     result->AddElement(new XmlElement(QN_STATUS));
     result->AddText(s.status(), 1);
 
+    if (!s.nick().empty()) {
+      result->AddElement(new XmlElement(QN_NICKNAME));
+      result->AddText(s.nick(), 1);
+    }
+
     std::string pri;
     talk_base::ToString(s.priority(), &pri);
 
diff --git a/talk/examples/call/presencepushtask.cc b/talk/examples/call/presencepushtask.cc
index 1e2cc52..320b99c 100644
--- a/talk/examples/call/presencepushtask.cc
+++ b/talk/examples/call/presencepushtask.cc
@@ -208,6 +208,11 @@
       std::string stamp = delay->Attr(kQnStamp);
       s->set_sent_time(stamp);
     }
+
+    const XmlElement* nick = stanza->FirstNamed(QN_NICKNAME);
+    if (nick) {
+      s->set_nick(nick->BodyText());
+    }
   }
 }
 
diff --git a/talk/examples/call/status.h b/talk/examples/call/status.h
index 68c333b..c3778e5 100644
--- a/talk/examples/call/status.h
+++ b/talk/examples/call/status.h
@@ -70,6 +70,7 @@
   int priority() const { return pri_; }
   Show show() const { return show_; }
   const std::string & status() const { return status_; }
+  const std::string & nick() const { return nick_; }
   bool available() const { return available_ ; }
   int error_code() const { return e_code_; }
   const std::string & error_string() const { return e_str_; }
@@ -87,6 +88,7 @@
   void set_priority(int pri) { pri_ = pri; }
   void set_show(Show show) { show_ = show; }
   void set_status(const std::string & status) { status_ = status; }
+  void set_nick(const std::string & nick) { nick_ = nick; }
   void set_available(bool a) { available_ = a; }
   void set_error(int e_code, const std::string e_str)
       { e_code_ = e_code; e_str_ = e_str; }
@@ -206,6 +208,7 @@
   int pri_;
   Show show_;
   std::string status_;
+  std::string nick_;
   bool available_;
   int e_code_;
   std::string e_str_;
diff --git a/talk/main.scons b/talk/main.scons
index f88d1a5..c8b0fd9 100644
--- a/talk/main.scons
+++ b/talk/main.scons
@@ -77,6 +77,14 @@
            'Whether the build system has the libpulse package')
 
 
+# List all the locales we localize to.
+root_env.AppendUnique(locales = [
+    'ar', 'bg', 'bn', 'ca', 'cs', 'da', 'de', 'el', 'en-GB', 'es', 'es-419',
+    'et', 'fa', 'fi', 'fil', 'fr', 'gu', 'hi', 'hr', 'hu', 'id', 'is', 'it',
+    'iw', 'ja', 'kn', 'ko', 'lt', 'lv', 'ml', 'mr', 'ms', 'nl', 'no', 'or',
+    'pl', 'pt-BR', 'pt-PT', 'ro', 'ru', 'sk', 'sl', 'sr', 'sv', 'ta', 'te',
+    'th', 'tl', 'tr', 'uk', 'ur', 'vi', 'zh-CN', 'zh-TW'])
+
 #-------------------------------------------------------------------------------
 # W I N D O W S
 #
diff --git a/talk/p2p/base/relayport.cc b/talk/p2p/base/relayport.cc
index 2d3be61..5348fe5 100644
--- a/talk/p2p/base/relayport.cc
+++ b/talk/p2p/base/relayport.cc
@@ -199,8 +199,10 @@
 
   set_username_fragment(username);
   set_password(password);
-  if (magic_cookie_.size() == 0)
-    magic_cookie_.append(STUN_MAGIC_COOKIE_VALUE, 4);
+  if (magic_cookie_.size() == 0) {
+    magic_cookie_.append(TURN_MAGIC_COOKIE_VALUE,
+                         sizeof(TURN_MAGIC_COOKIE_VALUE));
+  }
 }
 
 RelayPort::~RelayPort() {
@@ -535,7 +537,8 @@
 
   StunMessage request;
   request.SetType(STUN_SEND_REQUEST);
-  request.SetTransactionID(talk_base::CreateRandomString(16));
+  request.SetTransactionID(
+      talk_base::CreateRandomString(kStunTransactionIdLength));
 
   StunByteStringAttribute* magic_cookie_attr =
       StunAttribute::CreateByteString(STUN_ATTR_MAGIC_COOKIE);
diff --git a/talk/p2p/base/relayserver.cc b/talk/p2p/base/relayserver.cc
index 17d4227..82f382a 100644
--- a/talk/p2p/base/relayserver.cc
+++ b/talk/p2p/base/relayserver.cc
@@ -79,10 +79,12 @@
 
   StunByteStringAttribute* magic_cookie_attr =
       StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE);
-  if (magic_cookie.size() == 0)
-    magic_cookie_attr->CopyBytes(cricket::STUN_MAGIC_COOKIE_VALUE, 4);
-  else
+  if (magic_cookie.size() == 0) {
+    magic_cookie_attr->CopyBytes(cricket::TURN_MAGIC_COOKIE_VALUE,
+                                 sizeof(cricket::TURN_MAGIC_COOKIE_VALUE));
+  } else {
     magic_cookie_attr->CopyBytes(magic_cookie.c_str(), magic_cookie.size());
+  }
   err_msg.AddAttribute(magic_cookie_attr);
 
   StunErrorCodeAttribute* err_code = StunAttribute::CreateErrorCode();
@@ -610,7 +612,6 @@
 
   StunMessage msg;
   msg.SetType(STUN_DATA_INDICATION);
-  msg.SetTransactionID("0000000000000000");
 
   StunByteStringAttribute* magic_cookie_attr =
       StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE);
@@ -669,7 +670,8 @@
     lifetime_(lifetime) {
   // For now, every connection uses the standard magic cookie value.
   magic_cookie_.append(
-      reinterpret_cast<const char*>(STUN_MAGIC_COOKIE_VALUE), 4);
+      reinterpret_cast<const char*>(TURN_MAGIC_COOKIE_VALUE),
+      sizeof(TURN_MAGIC_COOKIE_VALUE));
 
   // Initialize the last-used time to now.
   NoteUsed();
diff --git a/talk/p2p/base/session.cc b/talk/p2p/base/session.cc
index e9c5a03..54ae1bc 100644
--- a/talk/p2p/base/session.cc
+++ b/talk/p2p/base/session.cc
@@ -868,8 +868,38 @@
 
 bool Session::OnUpdateMessage(const SessionMessage& msg,
                               MessageError* error) {
-  // TODO: Once someone needs it, parse the message
-  // into a data structure and signal out.
+  if (!CheckState(STATE_INPROGRESS, error))
+    return false;
+
+  DescriptionInfo description_info;
+  if (!ParseDescriptionInfo(msg.protocol, msg.action_elem,
+                            GetContentParsers(), GetTransportParsers(),
+                            &description_info, error)) {
+    return false;
+  }
+
+  ContentInfos updated_contents = description_info.ClearContents();
+  ContentInfos::iterator it;
+
+  // First, ensure all updates are valid before modifying remote_description_.
+  for (it = updated_contents.begin(); it != updated_contents.end(); ++it) {
+    if (remote_description_->GetContentByName(it->name) == NULL) {
+      return false;
+    }
+
+    // TODO: We should add a check to ensure that the updated
+    // contents are compatible with the original contents.
+  }
+
+  // Merge the updates into the remote description.
+  for (it = updated_contents.begin(); it != updated_contents.end(); ++it) {
+    LOG(LS_INFO) << "Updating content " << it->name;
+    remote_description_->RemoveContentByName(it->name);
+    remote_description_->AddContent(it->name, it->type, it->description);
+  }
+
+  SignalRemoteDescriptionUpdate(this);
+
   return true;
 }
 
diff --git a/talk/p2p/base/session.h b/talk/p2p/base/session.h
index c299ac6..bfafe77 100644
--- a/talk/p2p/base/session.h
+++ b/talk/p2p/base/session.h
@@ -233,7 +233,7 @@
     return remote_description_;
   }
   // Takes ownership of SessionDescription*
-  bool set_remote_description(const SessionDescription* sdesc) {
+  bool set_remote_description(SessionDescription* sdesc) {
     if (sdesc != remote_description_) {
       delete remote_description_;
       remote_description_ = sdesc;
@@ -273,11 +273,14 @@
 
   const std::string& id() const { return sid_; }
 
+  // Fired when the remote description is updated.
+  sigslot::signal1<BaseSession *> SignalRemoteDescriptionUpdate;
+
  protected:
   State state_;
   Error error_;
   const SessionDescription* local_description_;
-  const SessionDescription* remote_description_;
+  SessionDescription* remote_description_;
   std::string sid_;
   // We don't use buzz::Jid because changing to buzz:Jid here has a
   // cascading effect that requires an enormous number places to
diff --git a/talk/p2p/base/sessiondescription.cc b/talk/p2p/base/sessiondescription.cc
index 872358e..6554f5a 100644
--- a/talk/p2p/base/sessiondescription.cc
+++ b/talk/p2p/base/sessiondescription.cc
@@ -69,4 +69,17 @@
   contents_.push_back(ContentInfo(name, type, description));
 }
 
+bool SessionDescription::RemoveContentByName(const std::string& name) {
+  for (ContentInfos::iterator content = contents_.begin();
+       content != contents_.end(); ++content) {
+    if (content->name == name) {
+      delete content->description;
+      contents_.erase(content);
+      return true;
+    }
+  }
+
+  return false;
+}
+
 }  // namespace cricket
diff --git a/talk/p2p/base/sessiondescription.h b/talk/p2p/base/sessiondescription.h
index fe575fa..38c902b 100644
--- a/talk/p2p/base/sessiondescription.h
+++ b/talk/p2p/base/sessiondescription.h
@@ -75,9 +75,7 @@
   void AddContent(const std::string& name,
                   const std::string& type,
                   const ContentDescription* description);
-  // TODO: Implement RemoveContent when it's needed for
-  // content-remove Jingle messages.
-  // void RemoveContent(const std::string& name);
+  bool RemoveContentByName(const std::string& name);
   const ContentInfos& contents() const { return contents_; }
 
   ~SessionDescription() {
diff --git a/talk/p2p/base/sessionmessages.cc b/talk/p2p/base/sessionmessages.cc
index 7bc47b4..ae2e656 100644
--- a/talk/p2p/base/sessionmessages.cc
+++ b/talk/p2p/base/sessionmessages.cc
@@ -675,12 +675,13 @@
   return true;
 }
 
-bool ParseSessionInitiate(SignalingProtocol protocol,
-                          const buzz::XmlElement* action_elem,
-                          const ContentParserMap& content_parsers,
-                          const TransportParserMap& trans_parsers,
-                          SessionInitiate* init,
-                          ParseError* error) {
+static bool ParseContentMessage(
+    SignalingProtocol protocol,
+    const buzz::XmlElement* action_elem,
+    const ContentParserMap& content_parsers,
+    const TransportParserMap& trans_parsers,
+    SessionInitiate* init,
+    ParseError* error) {
   init->owns_contents = true;
   if (protocol == PROTOCOL_GINGLE) {
     if (!ParseGingleContentInfos(action_elem, content_parsers,
@@ -703,14 +704,14 @@
   return true;
 }
 
-
-bool WriteSessionInitiate(SignalingProtocol protocol,
-                          const ContentInfos& contents,
-                          const TransportInfos& tinfos,
-                          const ContentParserMap& content_parsers,
-                          const TransportParserMap& transport_parsers,
-                          XmlElements* elems,
-                          WriteError* error) {
+static bool WriteContentMessage(
+    SignalingProtocol protocol,
+    const ContentInfos& contents,
+    const TransportInfos& tinfos,
+    const ContentParserMap& content_parsers,
+    const TransportParserMap& transport_parsers,
+    XmlElements* elems,
+    WriteError* error) {
   if (protocol == PROTOCOL_GINGLE) {
     if (!WriteGingleContentInfos(contents, content_parsers, elems, error))
       return false;
@@ -728,15 +729,39 @@
   return true;
 }
 
+bool ParseSessionInitiate(SignalingProtocol protocol,
+                          const buzz::XmlElement* action_elem,
+                          const ContentParserMap& content_parsers,
+                          const TransportParserMap& trans_parsers,
+                          SessionInitiate* init,
+                          ParseError* error) {
+  return ParseContentMessage(protocol, action_elem,
+                             content_parsers, trans_parsers,
+                             init, error);
+}
+
+
+bool WriteSessionInitiate(SignalingProtocol protocol,
+                          const ContentInfos& contents,
+                          const TransportInfos& tinfos,
+                          const ContentParserMap& content_parsers,
+                          const TransportParserMap& transport_parsers,
+                          XmlElements* elems,
+                          WriteError* error) {
+  return WriteContentMessage(protocol, contents, tinfos,
+                             content_parsers, transport_parsers,
+                             elems, error);
+}
+
 bool ParseSessionAccept(SignalingProtocol protocol,
                         const buzz::XmlElement* action_elem,
                         const ContentParserMap& content_parsers,
                         const TransportParserMap& transport_parsers,
                         SessionAccept* accept,
                         ParseError* error) {
-  return ParseSessionInitiate(protocol, action_elem,
-                              content_parsers, transport_parsers,
-                              accept, error);
+  return ParseContentMessage(protocol, action_elem,
+                             content_parsers, transport_parsers,
+                             accept, error);
 }
 
 bool WriteSessionAccept(SignalingProtocol protocol,
@@ -746,9 +771,9 @@
                         const TransportParserMap& transport_parsers,
                         XmlElements* elems,
                         WriteError* error) {
-  return WriteSessionInitiate(protocol, contents, tinfos,
-                              content_parsers, transport_parsers,
-                              elems, error);
+  return WriteContentMessage(protocol, contents, tinfos,
+                             content_parsers, transport_parsers,
+                             elems, error);
 }
 
 bool ParseSessionTerminate(SignalingProtocol protocol,
@@ -794,6 +819,17 @@
   }
 }
 
+bool ParseDescriptionInfo(SignalingProtocol protocol,
+                          const buzz::XmlElement* action_elem,
+                          const ContentParserMap& content_parsers,
+                          const TransportParserMap& transport_parsers,
+                          DescriptionInfo* description_info,
+                          ParseError* error) {
+  return ParseContentMessage(protocol, action_elem,
+                             content_parsers, transport_parsers,
+                             description_info, error);
+}
+
 bool ParseTransportInfos(SignalingProtocol protocol,
                          const buzz::XmlElement* action_elem,
                          const ContentInfos& contents,
diff --git a/talk/p2p/base/sessionmessages.h b/talk/p2p/base/sessionmessages.h
index ef7cc69..214fa8c 100644
--- a/talk/p2p/base/sessionmessages.h
+++ b/talk/p2p/base/sessionmessages.h
@@ -114,10 +114,12 @@
 
 typedef std::vector<TransportInfo> TransportInfos;
 
-struct SessionInitiate {
-  SessionInitiate() : owns_contents(false) {}
+// TODO: Break up this class so we don't have to typedef it into
+// different classes.
+struct ContentMessage {
+  ContentMessage() : owns_contents(false) {}
 
-  ~SessionInitiate() {
+  ~ContentMessage() {
     if (owns_contents) {
       for (ContentInfos::iterator content = contents.begin();
            content != contents.end(); content++) {
@@ -139,8 +141,10 @@
   TransportInfos transports;
 };
 
-// Right now, a SessionAccept is functionally equivalent to a SessionInitiate.
-typedef SessionInitiate SessionAccept;
+typedef ContentMessage SessionInitiate;
+typedef ContentMessage SessionAccept;
+// Note that a DescriptionInfo does not have TransportInfos.
+typedef ContentMessage DescriptionInfo;
 
 struct SessionTerminate {
   SessionTerminate() {}
@@ -202,6 +206,12 @@
 void WriteSessionTerminate(SignalingProtocol protocol,
                            const SessionTerminate& term,
                            XmlElements* elems);
+bool ParseDescriptionInfo(SignalingProtocol protocol,
+                          const buzz::XmlElement* action_elem,
+                          const ContentParserMap& content_parsers,
+                          const TransportParserMap& transport_parsers,
+                          DescriptionInfo* description_info,
+                          ParseError* error);
 // Since a TransportInfo is not a transport-info message, and a
 // transport-info message is just a collection of TransportInfos, we
 // say Parse/Write TransportInfos for transport-info messages.
diff --git a/talk/p2p/base/stun.cc b/talk/p2p/base/stun.cc
index ad8b5ce..e324d7e 100644
--- a/talk/p2p/base/stun.cc
+++ b/talk/p2p/base/stun.cc
@@ -40,15 +40,21 @@
 const std::string STUN_ERROR_REASON_UNAUTHORIZED = "UNAUTHORIZED";
 const std::string STUN_ERROR_REASON_UNKNOWN_ATTRIBUTE = "UNKNOWN ATTRIBUTE";
 const std::string STUN_ERROR_REASON_STALE_CREDENTIALS = "STALE CREDENTIALS";
-const std::string STUN_ERROR_REASON_INTEGRITY_CHECK_FAILURE = "INTEGRITY CHECK FAILURE";
+const std::string STUN_ERROR_REASON_INTEGRITY_CHECK_FAILURE =
+    "INTEGRITY CHECK FAILURE";
 const std::string STUN_ERROR_REASON_MISSING_USERNAME = "MISSING USERNAME";
 const std::string STUN_ERROR_REASON_USE_TLS = "USE TLS";
 const std::string STUN_ERROR_REASON_SERVER_ERROR = "SERVER ERROR";
 const std::string STUN_ERROR_REASON_GLOBAL_FAILURE = "GLOBAL FAILURE";
 
-StunMessage::StunMessage() : type_(0), length_(0),
-    transaction_id_("0000000000000000") {
-  ASSERT(transaction_id_.size() == 16);
+const char kStunMagicCookie[] = { '\x21', '\x12', '\xA4', '\x42' };
+
+const char TURN_MAGIC_COOKIE_VALUE[] = { '\x72', '\xC6', '\x4B', '\xC6' };
+
+StunMessage::StunMessage()
+    : type_(0), length_(0),
+      transaction_id_("000000000000") {
+  ASSERT(IsValidTransactionId(transaction_id_));
   attrs_ = new std::vector<StunAttribute*>();
 }
 
@@ -58,8 +64,15 @@
   delete attrs_;
 }
 
+bool StunMessage::IsLegacy() const {
+  if (transaction_id_.size() == kStunLegacyTransactionIdLength)
+    return true;
+  ASSERT(transaction_id_.size() == kStunTransactionIdLength);
+  return false;
+}
+
 void StunMessage::SetTransactionID(const std::string& str) {
-  ASSERT(str.size() == 16);
+  ASSERT(IsValidTransactionId(str));
   transaction_id_ = str;
 }
 
@@ -154,10 +167,20 @@
   if (!buf->ReadUInt16(&length_))
     return false;
 
-  std::string transaction_id;
-  if (!buf->ReadString(&transaction_id, 16))
+  std::string magic_cookie;
+  if (!buf->ReadString(&magic_cookie, kStunMagicCookieLength))
     return false;
-  ASSERT(transaction_id.size() == 16);
+
+  std::string transaction_id;
+  if (!buf->ReadString(&transaction_id, kStunTransactionIdLength))
+    return false;
+  if (magic_cookie != std::string(kStunMagicCookie,
+                                  kStunMagicCookie + kStunMagicCookieLength)) {
+    // If magic cookie is invalid it means that the peer implements
+    // RFC3489 instead of RFC5389.
+    transaction_id.insert(0, magic_cookie);
+  }
+  ASSERT(IsValidTransactionId(transaction_id));
   transaction_id_ = transaction_id;
 
   if (length_ > buf->Length())
@@ -193,6 +216,8 @@
 void StunMessage::Write(ByteBuffer* buf) const {
   buf->WriteUInt16(type_);
   buf->WriteUInt16(length_);
+  if (!IsLegacy())
+    buf->WriteBytes(kStunMagicCookie, kStunMagicCookieLength);
   buf->WriteString(transaction_id_);
 
   for (unsigned i = 0; i < attrs_->size(); i++) {
@@ -202,6 +227,11 @@
   }
 }
 
+bool StunMessage::IsValidTransactionId(const std::string& transaction_id) {
+  return transaction_id.size() == kStunTransactionIdLength ||
+      transaction_id.size() == kStunLegacyTransactionIdLength;
+}
+
 StunAttribute::StunAttribute(uint16 type, uint16 length)
     : type_(type), length_(length) {
 }
diff --git a/talk/p2p/base/stun.h b/talk/p2p/base/stun.h
index 2282fed..cc0e972 100644
--- a/talk/p2p/base/stun.h
+++ b/talk/p2p/base/stun.h
@@ -25,8 +25,8 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef __STUN_H__
-#define __STUN_H__
+#ifndef TALK_P2P_BASE_STUN_H_
+#define TALK_P2P_BASE_STUN_H_
 
 // This file contains classes for dealing with the STUN and TURN protocols.
 // Both protocols use the same wire format.
@@ -39,6 +39,9 @@
 
 namespace cricket {
 
+// TODO: Remove message types and attributes that are no
+// longer suppored in RFC5389 (see section 18.2).
+
 // These are the types of STUN & TURN messages as of last check.
 enum StunMessageType {
   STUN_BINDING_REQUEST              = 0x0001,
@@ -103,6 +106,16 @@
 extern const std::string STUN_ERROR_REASON_SERVER_ERROR;
 extern const std::string STUN_ERROR_REASON_GLOBAL_FAILURE;
 
+// Following values correspond to RFC5389.
+const size_t kStunTransactionIdOffset = 8;
+const size_t kStunTransactionIdLength = 12;
+extern const char kStunMagicCookie[4];
+const size_t kStunMagicCookieLength = sizeof(kStunMagicCookie);
+
+// Following value corresponds to an earlier version of STUN from
+// RFC3489.
+const size_t kStunLegacyTransactionIdLength = 16;
+
 class StunAttribute;
 class StunAddressAttribute;
 class StunUInt32Attribute;
@@ -119,11 +132,17 @@
 public:
   StunMessage();
   ~StunMessage();
-
   StunMessageType type() const { return static_cast<StunMessageType>(type_); }
   uint16 length() const { return length_; }
   const std::string& transaction_id() const { return transaction_id_; }
 
+  // Returns true if the message confirms to RFC3489 rather than
+  // RFC5389. The main difference between two version of the STUN
+  // protocol is the presence of the magic cookie and different length
+  // of transaction ID. For outgoing packets version of the protocol
+  // is determined by the lengths of the transaction ID.
+  bool IsLegacy() const;
+
   void SetType(StunMessageType type) { type_ = type; }
   void SetTransactionID(const std::string& str);
 
@@ -140,7 +159,7 @@
   // return value indicates whether this was successful.
   bool Read(talk_base::ByteBuffer* buf);
 
-  // Writes this object into a STUN/TURN packet.  Return value is true if
+  // Writes this object into a STUN/TURN packet. Return value is true if
   // successful.
   void Write(talk_base::ByteBuffer* buf) const;
 
@@ -151,6 +170,7 @@
   std::vector<StunAttribute*>* attrs_;
 
   const StunAttribute* GetAttribute(StunAttributeType type) const;
+  static bool IsValidTransactionId(const std::string& transaction_id);
 };
 
 // Base class for all STUN/TURN attributes.
@@ -160,7 +180,7 @@
 
   StunAttributeType type() const {
     return static_cast<StunAttributeType>(type_);
-  } 
+  }
   uint16 length() const { return length_; }
 
   // Reads the body (not the type or length) for this type of attribute from
@@ -195,7 +215,7 @@
 // Implements STUN/TURN attributes that record an Internet address.
 class StunAddressAttribute : public StunAttribute {
 public:
-  StunAddressAttribute(uint16 type);
+  explicit StunAddressAttribute(uint16 type);
 
 #if (_MSC_VER < 1300)
   enum { SIZE = 8 };
@@ -223,7 +243,7 @@
 // Implements STUN/TURN attributs that record a 32-bit integer.
 class StunUInt32Attribute : public StunAttribute {
 public:
-  StunUInt32Attribute(uint16 type);
+  explicit StunUInt32Attribute(uint16 type);
 
 #if (_MSC_VER < 1300)
   enum { SIZE = 4 };
@@ -352,7 +372,9 @@
 
 // The special MAGIC-COOKIE attribute is used to distinguish TURN packets from
 // other kinds of traffic.
-const char STUN_MAGIC_COOKIE_VALUE[] = { 0x72, char(0xc6), 0x4b, char(0xc6) };
+// TODO: This value has nothing to do with STUN. Move it to a
+// separate file.
+extern const char TURN_MAGIC_COOKIE_VALUE[4];
 
 // Returns the (successful) response type for the given request type.
 StunMessageType GetStunResponseType(StunMessageType request_type);
@@ -362,4 +384,4 @@
 
 } // namespace cricket
 
-#endif // __STUN_H__
+#endif  // TALK_P2P_BASE_STUN_H_
diff --git a/talk/p2p/base/stunrequest.cc b/talk/p2p/base/stunrequest.cc
index 1ad121e..9c1ada7 100644
--- a/talk/p2p/base/stunrequest.cc
+++ b/talk/p2p/base/stunrequest.cc
@@ -110,7 +110,7 @@
     return false;
 
   std::string id;
-  id.append(data + 4, 16);
+  id.append(data + kStunTransactionIdOffset, kStunTransactionIdLength);
 
   RequestMap::iterator iter = requests_.find(id);
   if (iter == requests_.end())
@@ -128,7 +128,8 @@
 
 StunRequest::StunRequest()
     : count_(0), timeout_(false), manager_(0),
-      id_(talk_base::CreateRandomString(16)), msg_(new StunMessage()),
+      id_(talk_base::CreateRandomString(kStunTransactionIdLength)),
+      msg_(new StunMessage()),
       tstamp_(0) {
   msg_->SetTransactionID(id_);
 }
diff --git a/talk/session/phone/call.cc b/talk/session/phone/call.cc
index 251eafa..08658ff 100644
--- a/talk/session/phone/call.cc
+++ b/talk/session/phone/call.cc
@@ -138,7 +138,7 @@
   for (it = view_request.static_video_views.begin();
        it != view_request.static_video_views.end(); ++it) {
     const NamedSource* found_source =
-        media_sources.GetVideoSourceBySsrc(it->ssrc);
+        media_sources_.GetVideoSourceBySsrc(it->ssrc);
     if (!found_source) {
       LOG(LS_WARNING) <<
           "Tried sending view request for bad ssrc: " << it->ssrc;
@@ -547,11 +547,11 @@
     for (it = sources.audio.begin(); it != sources.audio.end(); ++it) {
       const NamedSource* found;
       if (it->ssrc_set) {
-        found = media_sources.GetAudioSourceBySsrc(it->ssrc);
+        found = media_sources_.GetAudioSourceBySsrc(it->ssrc);
       } else {
         // For backwards compatibility, we remove by nick.
         // TODO: Remove once all senders use explicit remove by ssrc.
-        found = media_sources.GetFirstAudioSourceByNick(it->nick);
+        found = media_sources_.GetFirstAudioSourceByNick(it->nick);
         if (found) {
           it->SetSsrc(found->ssrc);
           it->removed = true;
@@ -561,12 +561,12 @@
       }
       if (it->removed && found) {
         RemoveVoiceStream(session, found->ssrc);
-        media_sources.RemoveAudioSourceBySsrc(it->ssrc);
+        media_sources_.RemoveAudioSourceBySsrc(it->ssrc);
         updates.audio.push_back(*it);
         LOG(LS_INFO) << "Removed voice stream:  " << found->ssrc;
       } else if (!it->removed && !found) {
         AddVoiceStream(session, it->ssrc);
-        media_sources.AddAudioSource(*it);
+        media_sources_.AddAudioSource(*it);
         updates.audio.push_back(*it);
         LOG(LS_INFO) << "Added voice stream:  " << it->ssrc;
       }
@@ -574,11 +574,11 @@
     for (it = sources.video.begin(); it != sources.video.end(); ++it) {
       const NamedSource* found;
       if (it->ssrc_set) {
-        found = media_sources.GetVideoSourceBySsrc(it->ssrc);
+        found = media_sources_.GetVideoSourceBySsrc(it->ssrc);
       } else {
         // For backwards compatibility, we remove by nick.
         // TODO: Remove once all senders use explicit remove by ssrc.
-        found = media_sources.GetFirstVideoSourceByNick(it->nick);
+        found = media_sources_.GetFirstVideoSourceByNick(it->nick);
         if (found) {
           it->SetSsrc(found->ssrc);
           it->removed = true;
@@ -588,12 +588,12 @@
       }
       if (it->removed && found) {
         RemoveVideoStream(session, found->ssrc);
-        media_sources.RemoveVideoSourceBySsrc(it->ssrc);
+        media_sources_.RemoveVideoSourceBySsrc(it->ssrc);
         updates.video.push_back(*it);
         LOG(LS_INFO) << "Removed video stream:  " << found->ssrc;
       } else if (!it->removed && !found) {
         AddVideoStream(session, it->ssrc);
-        media_sources.AddVideoSource(*it);
+        media_sources_.AddVideoSource(*it);
         updates.video.push_back(*it);
         LOG(LS_INFO) << "Added video stream:  " << it->ssrc;
       }
diff --git a/talk/session/phone/call.h b/talk/session/phone/call.h
index 06c3981..38e0684 100644
--- a/talk/session/phone/call.h
+++ b/talk/session/phone/call.h
@@ -131,7 +131,7 @@
   uint32 id_;
   MediaSessionClient *session_client_;
   std::vector<Session *> sessions_;
-  MediaSources media_sources;
+  MediaSources media_sources_;
   std::map<std::string, VoiceChannel *> voice_channel_map_;
   std::map<std::string, VideoChannel *> video_channel_map_;
   VideoRenderer* local_renderer_;
diff --git a/talk/session/phone/channel.cc b/talk/session/phone/channel.cc
index bd5ec30..b23d877 100644
--- a/talk/session/phone/channel.cc
+++ b/talk/session/phone/channel.cc
@@ -101,6 +101,8 @@
   LOG(LS_INFO) << "Created channel";
 
   session->SignalState.connect(this, &BaseChannel::OnSessionState);
+  session->SignalRemoteDescriptionUpdate.connect(this,
+      &BaseChannel::OnRemoteDescriptionUpdate);
 }
 
 BaseChannel::~BaseChannel() {
@@ -425,6 +427,16 @@
   }
 }
 
+void BaseChannel::OnRemoteDescriptionUpdate(BaseSession* session) {
+  const MediaContentDescription* content =
+      GetFirstContent(session->remote_description());
+
+  if (content && !SetRemoteContent(content, CA_UPDATE)) {
+    LOG(LS_ERROR) << "Failure in SetRemoteContent with CA_UPDATE";
+    session->SetError(BaseSession::ERROR_CONTENT);
+  }
+}
+
 void BaseChannel::EnableMedia_w() {
   ASSERT(worker_thread_ == talk_base::Thread::Current());
   if (enabled_)
diff --git a/talk/session/phone/channel.h b/talk/session/phone/channel.h
index 9378a0f..7d6577a 100644
--- a/talk/session/phone/channel.h
+++ b/talk/session/phone/channel.h
@@ -176,6 +176,7 @@
 
   // Setting the send codec based on the remote description.
   void OnSessionState(BaseSession* session, BaseSession::State state);
+  void OnRemoteDescriptionUpdate(BaseSession* session);
 
   void EnableMedia_w();
   void DisableMedia_w();
diff --git a/talk/session/phone/mediaengine.cc b/talk/session/phone/mediaengine.cc
index a3eb227..43d75e1 100644
--- a/talk/session/phone/mediaengine.cc
+++ b/talk/session/phone/mediaengine.cc
@@ -33,15 +33,35 @@
 #ifdef 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
 #endif
 
 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
 
 MediaEngine* MediaEngine::Create() {
 #if defined(HAVE_LINPHONE)
   return new LinphoneMediaEngine("", "");
 #elif defined(HAVE_WEBRTC)
+#if defined(PLATFORM_CHROMIUM)
+  return new CompositeMediaEngine<ChromiumWebRtcVoiceEngine,
+                                  WebRtcVideoEngine>();
+#else
   return new CompositeMediaEngine<WebRtcVoiceEngine, WebRtcVideoEngine>();
+#endif
 #else
   return new NullMediaEngine();
 #endif
diff --git a/talk/session/phone/rtpdump.cc b/talk/session/phone/rtpdump.cc
index 2ac83eb..ad0e82d 100644
--- a/talk/session/phone/rtpdump.cc
+++ b/talk/session/phone/rtpdump.cc
@@ -86,6 +86,11 @@
       cricket::GetRtpSsrc(&data[0], data.size(), ssrc);
 }
 
+bool RtpDumpPacket::GetRtpHeaderLen(size_t* len) const {
+  return IsValidRtpPacket() &&
+      cricket::GetRtpHeaderLen(&data[0], data.size(), len);
+}
+
 bool RtpDumpPacket::GetRtcpType(int* type) const {
   return IsValidRtcpPacket() &&
       cricket::GetRtcpType(&data[0], data.size(), type);
@@ -287,6 +292,7 @@
 
 RtpDumpWriter::RtpDumpWriter(talk_base::StreamInterface* stream)
     : stream_(stream),
+      packet_filter_(PF_ALL),
       file_header_written_(false),
       start_time_ms_(talk_base::Time()) {
   }
@@ -295,6 +301,20 @@
   return talk_base::TimeSince(start_time_ms_);
 }
 
+talk_base::StreamResult RtpDumpWriter::WriteFileHeader() {
+  talk_base::StreamResult res = stream_->WriteAll(
+      RtpDumpFileHeader::kFirstLine.c_str(),
+      RtpDumpFileHeader::kFirstLine.size(), NULL, NULL);
+  if (res != talk_base::SR_SUCCESS) {
+    return res;
+  }
+
+  talk_base::ByteBuffer buf;
+  RtpDumpFileHeader file_header(talk_base::Time(), 0, 0);
+  file_header.WriteToByteBuffer(&buf);
+  return stream_->WriteAll(buf.Data(), buf.Length(), NULL, NULL);
+}
+
 talk_base::StreamResult RtpDumpWriter::WritePacket(
     const void* data, size_t data_len, uint32 elapsed, bool rtcp) {
   if (!stream_ || !data || 0 == data_len) return talk_base::SR_ERROR;
@@ -309,9 +329,16 @@
     file_header_written_ = true;
   }
 
+  // Figure out what to write.
+  size_t write_len = FilterPacket(data, data_len, rtcp);
+  if (write_len == 0) {
+    return talk_base::SR_SUCCESS;
+  }
+
   // Write the dump packet header.
   talk_base::ByteBuffer buf;
-  buf.WriteUInt16(static_cast<uint16>(RtpDumpPacket::kHeaderLength + data_len));
+  buf.WriteUInt16(static_cast<uint16>(
+                      RtpDumpPacket::kHeaderLength + write_len));
   buf.WriteUInt16(static_cast<uint16>(rtcp ? 0 : data_len));
   buf.WriteUInt32(elapsed);
   res = stream_->WriteAll(buf.Data(), buf.Length(), NULL, NULL);
@@ -319,22 +346,32 @@
     return res;
   }
 
-  // Write the actual RTP or RTCP packet.
-  return stream_->WriteAll(data, data_len, NULL, NULL);
+  // Write the header or full packet as indicated by write_len.
+  return stream_->WriteAll(data, write_len, NULL, NULL);
 }
 
-talk_base::StreamResult RtpDumpWriter::WriteFileHeader() {
-  talk_base::StreamResult res = stream_->WriteAll(
-      RtpDumpFileHeader::kFirstLine.c_str(),
-      RtpDumpFileHeader::kFirstLine.size(), NULL, NULL);
-  if (res != talk_base::SR_SUCCESS) {
-    return res;
+size_t RtpDumpWriter::FilterPacket(const void* data, size_t data_len,
+                                   bool rtcp) {
+  size_t filtered_len = 0;
+  if (!rtcp) {
+    if ((packet_filter_ & PF_RTPPACKET) == PF_RTPPACKET) {
+      // RTP header + payload
+      filtered_len = data_len;
+    } else if ((packet_filter_ & PF_RTPHEADER) == PF_RTPHEADER) {
+      // RTP header only
+      size_t header_len;
+      if (GetRtpHeaderLen(data, data_len, &header_len)) {
+        filtered_len = header_len;
+      }
+    }
+  } else {
+    if ((packet_filter_ & PF_RTCPPACKET) == PF_RTCPPACKET) {
+      // RTCP header + payload
+      filtered_len = data_len;
+    }
   }
 
-  talk_base::ByteBuffer buf;
-  RtpDumpFileHeader file_header(talk_base::Time(), 0, 0);
-  file_header.WriteToByteBuffer(&buf);
-  return stream_->WriteAll(buf.Data(), buf.Length(), NULL, NULL);
+  return filtered_len;
 }
 
 }  // namespace cricket
diff --git a/talk/session/phone/rtpdump.h b/talk/session/phone/rtpdump.h
index 5cc36a6..6b9f5a0 100644
--- a/talk/session/phone/rtpdump.h
+++ b/talk/session/phone/rtpdump.h
@@ -48,6 +48,15 @@
 // For each packet, the file contains a 8 byte dump packet header, followed by
 // the actual RTP or RTCP packet.
 
+enum RtpDumpPacketFilter {
+  PF_NONE = 0x0,
+  PF_RTPHEADER = 0x1,
+  PF_RTPPACKET = 0x3,  // includes header
+  // PF_RTCPHEADER = 0x4,  // TODO
+  PF_RTCPPACKET = 0xC,  // includes header
+  PF_ALL = 0xF
+};
+
 struct RtpDumpFileHeader {
   RtpDumpFileHeader(uint32 start_ms, uint32 s, uint16 p);
   void WriteToByteBuffer(talk_base::ByteBuffer* buf);
@@ -79,6 +88,7 @@
   bool GetRtpSeqNum(int* seq_num) const;
   bool GetRtpTimestamp(uint32* ts) const;
   bool GetRtpSsrc(uint32* ssrc) const;
+  bool GetRtpHeaderLen(size_t* len) const;
   // Get the type of the RTCP packet. Return true and set the output parameter
   // if successful.
   bool GetRtcpType(int* type) const;
@@ -170,6 +180,11 @@
  public:
   explicit RtpDumpWriter(talk_base::StreamInterface* stream);
 
+  // Filter to control what packets we actually record.
+  void set_packet_filter(int filter) {
+    packet_filter_ = filter;
+  }
+
   // Write a RTP or RTCP packet. The parameters data points to the packet and
   // data_len is its length.
   talk_base::StreamResult WriteRtpPacket(const void* data, size_t data_len) {
@@ -196,8 +211,10 @@
  private:
   talk_base::StreamResult WritePacket(const void* data, size_t data_len,
                                       uint32 elapsed, bool rtcp);
+  size_t FilterPacket(const void* data, size_t data_len, bool rtcp);
 
   talk_base::StreamInterface* stream_;
+  int packet_filter_;
   bool file_header_written_;
   uint32 start_time_ms_;  // Time when the record starts.
   DISALLOW_COPY_AND_ASSIGN(RtpDumpWriter);
diff --git a/talk/session/phone/rtputils.cc b/talk/session/phone/rtputils.cc
index 5e32c8b..e37c390 100644
--- a/talk/session/phone/rtputils.cc
+++ b/talk/session/phone/rtputils.cc
@@ -54,6 +54,23 @@
   return true;
 }
 
+bool GetRtpHeaderLen(const void* data, size_t len, size_t* value) {
+  if (!data || len < kMinRtpPacketLen || !value) return false;
+  const uint8* header = static_cast<const uint8*>(data);
+  // Get base header size + length of CSRCs (not counting extension yet).
+  size_t header_size = kMinRtpPacketLen + (header[0] & 0xF) * sizeof(uint32);
+  if (len < header_size) return false;
+  // If there's an extension, read and add in the extension size.
+  if (header[0] & 0x10) {
+    if (len < header_size + sizeof(uint32)) return false;
+    header_size += ((talk_base::GetBE16(header + header_size + 2) + 1) *
+                    sizeof(uint32));
+    if (len < header_size) return false;
+  }
+  *value = header_size;
+  return true;
+}
+
 bool GetRtcpType(const void* data, size_t len, int* value) {
   if (!data || len < kMinRtcpPacketLen || !value) return false;
   *value = static_cast<int>(*(static_cast<const uint8*>(data) + 1));
diff --git a/talk/session/phone/rtputils.h b/talk/session/phone/rtputils.h
index 1ac3214..69daa83 100644
--- a/talk/session/phone/rtputils.h
+++ b/talk/session/phone/rtputils.h
@@ -40,6 +40,7 @@
 bool GetRtpSeqNum(const void* data, size_t len, int* value);
 bool GetRtpTimestamp(const void* data, size_t len, uint32* value);
 bool GetRtpSsrc(const void* data, size_t len, uint32* value);
+bool GetRtpHeaderLen(const void* data, size_t len, size_t* value);
 bool GetRtcpType(const void* data, size_t len, int* value);
 
 }  // namespace cricket
diff --git a/talk/session/phone/srtpfilter.cc b/talk/session/phone/srtpfilter.cc
index fe91e5c..10ccfea 100644
--- a/talk/session/phone/srtpfilter.cc
+++ b/talk/session/phone/srtpfilter.cc
@@ -172,9 +172,9 @@
   return recv_session_->UnprotectRtcp(p, in_len, out_len);
 }
 
-void SrtpFilter::set_signal_silent_time(int signal_silent_time) {
-  send_session_->set_signal_silent_time(signal_silent_time);
-  recv_session_->set_signal_silent_time(signal_silent_time);
+void SrtpFilter::set_signal_silent_time(uint32 signal_silent_time_in_ms) {
+  send_session_->set_signal_silent_time(signal_silent_time_in_ms);
+  recv_session_->set_signal_silent_time(signal_silent_time_in_ms);
 }
 
 bool SrtpFilter::StoreParams(const std::vector<CryptoParams>& params,
@@ -386,8 +386,8 @@
   return true;
 }
 
-void SrtpSession::set_signal_silent_time(int signal_silent_time) {
-  srtp_stat_->set_signal_silent_time(signal_silent_time);
+void SrtpSession::set_signal_silent_time(uint32 signal_silent_time_in_ms) {
+  srtp_stat_->set_signal_silent_time(signal_silent_time_in_ms);
 }
 
 bool SrtpSession::SetKey(int type, const std::string& cs,
@@ -531,7 +531,7 @@
   return SrtpNotAvailable(__FUNCTION__);
 }
 
-void SrtpSession::set_signal_silent_time(int signal_silent_time) {
+void SrtpSession::set_signal_silent_time(uint32 signal_silent_time) {
   // Do nothing.
 }
 
@@ -585,39 +585,11 @@
 }
 
 void SrtpStat::AddProtectRtcpResult(int result) {
-  FailureKey key;
-  key.mode = SrtpFilter::PROTECT;
-  switch (result) {
-    case err_status_ok:
-      key.error = SrtpFilter::ERROR_NONE;
-      break;
-    case err_status_auth_fail:
-      key.error = SrtpFilter::ERROR_AUTH;
-      break;
-    default:
-      key.error = SrtpFilter::ERROR_FAIL;
-  }
-  HandleSrtpResult(key);
+  AddProtectRtpResult(0U, result);
 }
 
 void SrtpStat::AddUnprotectRtcpResult(int result) {
-  FailureKey key;
-  key.mode = SrtpFilter::UNPROTECT;
-  switch (result) {
-    case err_status_ok:
-      key.error = SrtpFilter::ERROR_NONE;
-      break;
-    case err_status_auth_fail:
-      key.error = SrtpFilter::ERROR_AUTH;
-      break;
-    case err_status_replay_fail:
-    case err_status_replay_old:
-      key.error = SrtpFilter::ERROR_REPLAY;
-      break;
-    default:
-      key.error = SrtpFilter::ERROR_FAIL;
-  }
-  HandleSrtpResult(key);
+  AddUnprotectRtpResult(0U, result);
 }
 
 void SrtpStat::HandleSrtpResult(const SrtpStat::FailureKey& key) {
@@ -630,7 +602,7 @@
     uint32 current_time = talk_base::Time();
     if (stat->last_signal_time == 0 ||
         talk_base::TimeDiff(current_time, stat->last_signal_time) >
-        signal_silent_time_) {
+        static_cast<int>(signal_silent_time_)) {
       SignalSrtpError(key.ssrc, key.mode, key.error);
       stat->last_signal_time = current_time;
     }
diff --git a/talk/session/phone/srtpfilter.h b/talk/session/phone/srtpfilter.h
index 9cae3c3..c59ee59 100644
--- a/talk/session/phone/srtpfilter.h
+++ b/talk/session/phone/srtpfilter.h
@@ -107,8 +107,8 @@
   bool UnprotectRtp(void* data, int in_len, int* out_len);
   bool UnprotectRtcp(void* data, int in_len, int* out_len);
 
-  // Update the silent threshold for signaling errors.
-  void set_signal_silent_time(int signal_silent_time);
+  // Update the silent threshold (in ms) for signaling errors.
+  void set_signal_silent_time(uint32 signal_silent_time_in_ms);
 
   sigslot::repeater3<uint32, Mode, Error> SignalSrtpError;
 
@@ -152,8 +152,8 @@
   bool UnprotectRtp(void* data, int in_len, int* out_len);
   bool UnprotectRtcp(void* data, int in_len, int* out_len);
 
-  // Update the silent threshold for signaling errors.
-  void set_signal_silent_time(int signal_silent_time);
+  // Update the silent threshold (in ms) for signaling errors.
+  void set_signal_silent_time(uint32 signal_silent_time_in_ms);
 
   sigslot::repeater3<uint32, SrtpFilter::Mode, SrtpFilter::Error>
       SignalSrtpError;
@@ -171,21 +171,31 @@
   static bool inited_;
   static std::list<SrtpSession*> sessions_;
   int last_send_seq_num_;
+  DISALLOW_COPY_AND_ASSIGN(SrtpSession);
 };
 
 // Class that collects failures of SRTP.
 class SrtpStat {
  public:
   SrtpStat();
+
+  // Report RTP protection results to the handler.
   void AddProtectRtpResult(uint32 ssrc, int result);
+  // Report RTP unprotection results to the handler.
   void AddUnprotectRtpResult(uint32 ssrc, int result);
+  // Report RTCP protection results to the handler.
   void AddProtectRtcpResult(int result);
+  // Report RTCP unprotection results to the handler.
   void AddUnprotectRtcpResult(int result);
+
+  // Get silent time (in ms) for SRTP statistics handler.
   uint32 signal_silent_time() const { return signal_silent_time_; }
+  // Set silent time (in ms) for SRTP statistics handler.
   void set_signal_silent_time(uint32 signal_silent_time) {
     signal_silent_time_ = signal_silent_time;
   }
 
+  // Sigslot for reporting errors.
   sigslot::signal3<uint32, SrtpFilter::Mode, SrtpFilter::Error>
       SignalSrtpError;
 
@@ -226,13 +236,14 @@
     uint32 last_signal_time;
   };
 
+  // Inspect SRTP result and signal error if needed.
   void HandleSrtpResult(const FailureKey& key);
 
   std::map<FailureKey, FailureStat> failures_;
   // Threshold in ms to silent the signaling errors.
-  int signal_silent_time_;
+  uint32 signal_silent_time_;
 
-  DISALLOW_EVIL_CONSTRUCTORS(SrtpStat);
+  DISALLOW_COPY_AND_ASSIGN(SrtpStat);
 };
 
 }  // namespace cricket
diff --git a/talk/xmpp/constants.cc b/talk/xmpp/constants.cc
index 647658a..2d108ac 100644
--- a/talk/xmpp/constants.cc
+++ b/talk/xmpp/constants.cc
@@ -290,9 +290,9 @@
 const QName QN_VCARD_AVATAR_HASH(true, NS_AVATAR_HASH, "hash");
 const QName QN_VCARD_AVATAR_HASH_MODIFIED(true, NS_AVATAR_HASH, "modified");
 
-const buzz::QName QN_NAME(true, STR_EMPTY, "name");
-const buzz::QName QN_AFFILIATION(true, STR_EMPTY, "affiliation");
-const buzz::QName QN_ROLE(true, STR_EMPTY, "role");
+const QName QN_NAME(true, STR_EMPTY, "name");
+const QName QN_AFFILIATION(true, STR_EMPTY, "affiliation");
+const QName QN_ROLE(true, STR_EMPTY, "role");
 
 #if defined(FEATURE_ENABLE_PSTN)
 const QName QN_VCARD_TEL(true, NS_VCARD, "TEL");
@@ -324,6 +324,7 @@
 const QName QN_TITLE1(true, STR_EMPTY, "title1");
 const QName QN_TITLE2(true, STR_EMPTY, "title2");
 const QName QN_SOURCE(true, STR_EMPTY, "source");
+const QName QN_TIME(true, STR_EMPTY, "time");
 
 const QName QN_XMLNS_CLIENT(true, NS_XMLNS, STR_CLIENT);
 const QName QN_XMLNS_SERVER(true, NS_XMLNS, STR_SERVER);
@@ -419,8 +420,8 @@
 const QName kQnVCardPhoto(true, kNSVCard, "photo");
 
 // JEP 0172 User Nickname
-const std::string kNSNickname("http://jabber.org/protocol/nick");
-const QName kQnNickname(true, kNSNickname, "nick");
+const std::string NS_NICKNAME("http://jabber.org/protocol/nick");
+const QName QN_NICKNAME(true, NS_NICKNAME, "nick");
 
 
 // JEP 0085 chat state
diff --git a/talk/xmpp/constants.h b/talk/xmpp/constants.h
index ae3abe4..07e8554 100644
--- a/talk/xmpp/constants.h
+++ b/talk/xmpp/constants.h
@@ -289,6 +289,7 @@
 extern const QName QN_TITLE2;
 extern const QName QN_AFFILIATION;
 extern const QName QN_ROLE;
+extern const QName QN_TIME;
 
 
 extern const QName QN_XMLNS_CLIENT;
@@ -386,8 +387,8 @@
 extern const QName kQnVCardPhoto;
 
 // JEP 0172 User Nickname
-extern const std::string kNSNickname;
-extern const QName kQnNickname;
+extern const std::string NS_NICKNAME;
+extern const QName QN_NICKNAME;
 
 
 // JEP 0085 chat state
diff --git a/talk/xmpp/xmpptask.h b/talk/xmpp/xmpptask.h
index a821d3c..86fbe27 100644
--- a/talk/xmpp/xmpptask.h
+++ b/talk/xmpp/xmpptask.h
@@ -93,7 +93,6 @@
   virtual void Stop();
   virtual bool HandleStanza(const XmlElement* stanza) { return false; }
   virtual void OnDisconnect();
-  virtual int ProcessReponse() { return STATE_DONE; }
 
   virtual void QueueStanza(const XmlElement* stanza);
   const XmlElement* NextStanza();