Update libjingle to 47547184.

R=mallinath@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/1617004

git-svn-id: http://libjingle.googlecode.com/svn/trunk@338 dd674b97-3498-5ee5-1854-bdd07cd0ff33
diff --git a/talk/app/webrtc/java/jni/peerconnection_jni.cc b/talk/app/webrtc/java/jni/peerconnection_jni.cc
index 9db8698..e0d5c09 100644
--- a/talk/app/webrtc/java/jni/peerconnection_jni.cc
+++ b/talk/app/webrtc/java/jni/peerconnection_jni.cc
@@ -62,6 +62,7 @@
 #include "talk/app/webrtc/peerconnectioninterface.h"
 #include "talk/app/webrtc/videosourceinterface.h"
 #include "talk/base/logging.h"
+#include "talk/base/ssladapter.h"
 #include "talk/media/base/videocapturer.h"
 #include "talk/media/base/videorenderer.h"
 #include "talk/media/devices/videorendererfactory.h"
@@ -891,6 +892,8 @@
   g_jvm = jvm;
   CHECK(g_jvm, "JNI_OnLoad handed NULL?");
 
+  CHECK(talk_base::InitializeSSL(), "Failed to InitializeSSL()");
+
   JNIEnv* jni;
   if (jvm->GetEnv(reinterpret_cast<void**>(&jni), JNI_VERSION_1_6) != JNI_OK)
     return -1;
@@ -914,6 +917,7 @@
   webrtc::Trace::ReturnTrace();
   delete g_class_reference_holder;
   g_class_reference_holder = NULL;
+  CHECK(talk_base::CleanupSSL(), "Failed to CleanupSSL()");
 }
 
 JOW(void, PeerConnection_freePeerConnection)(JNIEnv*, jclass, jlong j_p) {
diff --git a/talk/base/messagequeue.cc b/talk/base/messagequeue.cc
index cbb1ffa..5c40622 100644
--- a/talk/base/messagequeue.cc
+++ b/talk/base/messagequeue.cc
@@ -160,17 +160,6 @@
   return true;
 }
 
-struct MessageDeleter {
-  ~MessageDeleter() {
-    while (!messages.empty()) {
-      ASSERT(NULL == messages.front().phandler);
-      delete messages.front().pdata;
-      messages.pop_front();
-    }
-  }
-  MessageList messages;
-};
-
 bool MessageQueue::Get(Message *pmsg, int cmsWait, bool process_io) {
   // Return and clear peek if present
   // Always return the peek if it exists so there is Peek/Get symmetry
@@ -189,49 +178,55 @@
   uint32 msCurrent = msStart;
   while (true) {
     // Check for sent messages
-
     ReceiveSends();
 
-    // Check queues
-
+    // Check for posted events
     int cmsDelayNext = kForever;
-    {
-      MessageDeleter deleter;
-      CritScope cs(&crit_);
-
-      // Check for delayed messages that have been triggered
-      // Calc the next trigger too
-
-      while (!dmsgq_.empty()) {
-        if (TimeIsLater(msCurrent, dmsgq_.top().msTrigger_)) {
-          cmsDelayNext = TimeDiff(dmsgq_.top().msTrigger_, msCurrent);
-          break;
-        }
-        msgq_.push_back(dmsgq_.top().msg_);
-        dmsgq_.pop();
-      }
-
-      // Check for posted events
-
-      while (!msgq_.empty()) {
-        *pmsg = msgq_.front();
-        if (pmsg->ts_sensitive) {
-          long delay = TimeDiff(msCurrent, pmsg->ts_sensitive);
-          if (delay > 0) {
-            LOG_F(LS_WARNING) << "id: " << pmsg->message_id << "  delay: "
-                              << (delay + kMaxMsgLatency) << "ms";
+    bool first_pass = true;
+    while (true) {
+      // All queue operations need to be locked, but nothing else in this loop
+      // (specifically handling disposed message) can happen inside the crit.
+      // Otherwise, disposed MessageHandlers will cause deadlocks.
+      {
+        CritScope cs(&crit_);
+        // On the first pass, check for delayed messages that have been
+        // triggered and calculate the next trigger time.
+        if (first_pass) {
+          first_pass = false;
+          while (!dmsgq_.empty()) {
+            if (TimeIsLater(msCurrent, dmsgq_.top().msTrigger_)) {
+              cmsDelayNext = TimeDiff(dmsgq_.top().msTrigger_, msCurrent);
+              break;
+            }
+            msgq_.push_back(dmsgq_.top().msg_);
+            dmsgq_.pop();
           }
         }
-        msgq_.pop_front();
-        if (MQID_DISPOSE == pmsg->message_id) {
-          // Delete the object, but *not inside the crit scope!*.
-          deleter.messages.push_back(*pmsg);
-          // To be safe, make sure we don't return this message.
-          *pmsg = Message();
-          continue;
+        // Pull a message off the message queue, if available.
+        if (msgq_.empty()) {
+          break;
+        } else {
+          *pmsg = msgq_.front();
+          msgq_.pop_front();
         }
-        return true;
+      }  // crit_ is released here.
+
+      // Log a warning for time-sensitive messages that we're late to deliver.
+      if (pmsg->ts_sensitive) {
+        int32 delay = TimeDiff(msCurrent, pmsg->ts_sensitive);
+        if (delay > 0) {
+          LOG_F(LS_WARNING) << "id: " << pmsg->message_id << "  delay: "
+                            << (delay + kMaxMsgLatency) << "ms";
+        }
       }
+      // If this was a dispose message, delete it and skip it.
+      if (MQID_DISPOSE == pmsg->message_id) {
+        ASSERT(NULL == pmsg->phandler);
+        delete pmsg->pdata;
+        *pmsg = Message();
+        continue;
+      }
+      return true;
     }
 
     if (fStop_)
diff --git a/talk/base/messagequeue_unittest.cc b/talk/base/messagequeue_unittest.cc
index 78f7788..8e55548 100644
--- a/talk/base/messagequeue_unittest.cc
+++ b/talk/base/messagequeue_unittest.cc
@@ -106,3 +106,27 @@
   EXPECT_TRUE(deleted);
   EXPECT_FALSE(was_locked);
 }
+
+class DeletedMessageHandler : public MessageHandler {
+ public:
+  explicit DeletedMessageHandler(bool* deleted) : deleted_(deleted) { }
+  ~DeletedMessageHandler() {
+    *deleted_ = true;
+  }
+  void OnMessage(Message* msg) { }
+ private:
+  bool* deleted_;
+};
+
+TEST_F(MessageQueueTest, DiposeHandlerWithPostedMessagePending) {
+  bool deleted = false;
+  DeletedMessageHandler *handler = new DeletedMessageHandler(&deleted);
+  // First, post a dispose.
+  Dispose(handler);
+  // Now, post a message, which should *not* be returned by Get().
+  Post(handler, 1);
+  Message msg;
+  EXPECT_FALSE(Get(&msg, 0));
+  EXPECT_TRUE(deleted);
+}
+
diff --git a/talk/examples/android/src/org/appspot/apprtc/AppRTCClient.java b/talk/examples/android/src/org/appspot/apprtc/AppRTCClient.java
index 1807359..036e97e 100644
--- a/talk/examples/android/src/org/appspot/apprtc/AppRTCClient.java
+++ b/talk/examples/android/src/org/appspot/apprtc/AppRTCClient.java
@@ -34,6 +34,7 @@
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
+import org.webrtc.MediaConstraints;
 import org.webrtc.PeerConnection;
 
 import java.io.IOException;
@@ -122,6 +123,10 @@
     return appRTCSignalingParameters.initiator;
   }
 
+  public MediaConstraints pcConstraints() {
+    return appRTCSignalingParameters.pcConstraints;
+  }
+
   // Struct holding the signaling parameters of an AppRTC room.
   private class AppRTCSignalingParameters {
     public final List<PeerConnection.IceServer> iceServers;
@@ -129,15 +134,18 @@
     public final String channelToken;
     public final String postMessageUrl;
     public final boolean initiator;
+    public final MediaConstraints pcConstraints;
+
     public AppRTCSignalingParameters(
         List<PeerConnection.IceServer> iceServers,
         String gaeBaseHref, String channelToken, String postMessageUrl,
-        boolean initiator) {
+        boolean initiator, MediaConstraints pcConstraints) {
       this.iceServers = iceServers;
       this.gaeBaseHref = gaeBaseHref;
       this.channelToken = channelToken;
       this.postMessageUrl = postMessageUrl;
       this.initiator = initiator;
+      this.pcConstraints = pcConstraints;
     }
   }
 
@@ -250,8 +258,42 @@
             requestTurnServer(getVarValue(roomHtml, "turnUrl", true)));
       }
 
+      MediaConstraints pcConstraints = constraintsFromJSON(
+          getVarValue(roomHtml, "pcConstraints", false));
+
       return new AppRTCSignalingParameters(
-          iceServers, gaeBaseHref, token, postMessageUrl, initiator);
+          iceServers, gaeBaseHref, token, postMessageUrl, initiator,
+          pcConstraints);
+    }
+
+    private MediaConstraints constraintsFromJSON(String jsonString) {
+      try {
+        MediaConstraints constraints = new MediaConstraints();
+        JSONObject json = new JSONObject(jsonString);
+        if (json.has("mandatory")) {
+          JSONObject mandatoryJSON = json.getJSONObject("mandatory");
+          JSONArray mandatoryKeys = mandatoryJSON.names();
+          for (int i = 0; i < mandatoryKeys.length(); ++i) {
+            String key = (String) mandatoryKeys.getString(i);
+            String value = mandatoryJSON.getString(key);
+            constraints.mandatory.add(
+                new MediaConstraints.KeyValuePair(key, value));
+          }
+        }
+        if (json.has("optional")) {
+          JSONArray optionalJSON = json.getJSONArray("optional");
+          for (int i = 0; i < optionalJSON.length(); ++i) {
+            JSONObject keyValueDict = optionalJSON.getJSONObject(i);
+            String key = keyValueDict.names().getString(0);
+            String value = keyValueDict.getString(key);
+            constraints.optional.add(
+                new MediaConstraints.KeyValuePair(key, value));
+          }
+        }
+        return constraints;
+      } catch (JSONException e) {
+        throw new RuntimeException(e);
+      }
     }
 
     // Scan |roomHtml| for declaration & assignment of |varName| and return its
diff --git a/talk/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java b/talk/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java
index 020ab49..70b2594 100644
--- a/talk/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java
+++ b/talk/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java
@@ -167,7 +167,7 @@
     PeerConnectionFactory factory = new PeerConnectionFactory();
 
     pc = factory.createPeerConnection(
-        iceServers, new MediaConstraints(), pcObserver);
+        iceServers, appRtcClient.pcConstraints(), pcObserver);
 
     {
       final PeerConnection finalPC = pc;
diff --git a/talk/media/devices/devicemanager_unittest.cc b/talk/media/devices/devicemanager_unittest.cc
index 02bd273..d8564ea 100644
--- a/talk/media/devices/devicemanager_unittest.cc
+++ b/talk/media/devices/devicemanager_unittest.cc
@@ -362,6 +362,8 @@
 }
 #endif  // LINUX
 
+// TODO(noahric): These are flaky on windows on headless machines.
+#ifndef WIN32
 TEST(DeviceManagerTest, GetWindows) {
   if (!talk_base::WindowPickerFactory::IsSupported()) {
     LOG(LS_INFO) << "skipping test: window capturing is not supported with "
@@ -403,6 +405,7 @@
       descriptions.front().id()));
   EXPECT_FALSE(capturer.get() == NULL);
 }
+#endif  // !WIN32
 
 TEST_F(DeviceManagerTestFake, CaptureConstraintsWhitelisted) {
   const Device device("white", "white_id");
diff --git a/talk/media/webrtc/fakewebrtcvoiceengine.h b/talk/media/webrtc/fakewebrtcvoiceengine.h
index a273deb..b2b92d0 100644
--- a/talk/media/webrtc/fakewebrtcvoiceengine.h
+++ b/talk/media/webrtc/fakewebrtcvoiceengine.h
@@ -631,16 +631,21 @@
   }
   WEBRTC_STUB(GetRemoteSSRC, (int channel, unsigned int& ssrc));
   WEBRTC_FUNC(SetRTPAudioLevelIndicationStatus, (int channel, bool enable,
-      unsigned char ID)) {
+      unsigned char id)) {
     WEBRTC_CHECK_CHANNEL(channel);
-    channels_[channel]->level_header_ext_ = (enable) ? ID : -1;
+    if (enable && (id < 1 || id > 14)) {
+      // [RFC5285] The 4-bit ID is the local identifier of this element in
+      // the range 1-14 inclusive.
+      return -1;
+    }
+    channels_[channel]->level_header_ext_ = (enable) ? id : -1;
     return 0;
   }
   WEBRTC_FUNC(GetRTPAudioLevelIndicationStatus, (int channel, bool& enabled,
-      unsigned char& ID)) {
+      unsigned char& id)) {
     WEBRTC_CHECK_CHANNEL(channel);
     enabled = (channels_[channel]->level_header_ext_ != -1);
-    ID = channels_[channel]->level_header_ext_;
+    id = channels_[channel]->level_header_ext_;
     return 0;
   }
   WEBRTC_STUB(GetRemoteCSRCs, (int channel, unsigned int arrCSRC[15]));
@@ -715,6 +720,9 @@
     redPayloadtype = channels_[channel]->fec_type;
     return 0;
   }
+#ifdef USE_WEBRTC_DEV_BRANCH
+  WEBRTC_STUB(SetNACKStatus, (int channel, bool enable, int maxNoPackets));
+#endif
   WEBRTC_STUB(StartRTPDump, (int channel, const char* fileNameUTF8,
                              webrtc::RTPDirections direction));
   WEBRTC_STUB(StopRTPDump, (int channel, webrtc::RTPDirections direction));
diff --git a/talk/media/webrtc/webrtcvoiceengine.cc b/talk/media/webrtc/webrtcvoiceengine.cc
index e78ffec..70fb1cf 100644
--- a/talk/media/webrtc/webrtcvoiceengine.cc
+++ b/talk/media/webrtc/webrtcvoiceengine.cc
@@ -1824,17 +1824,11 @@
   }
 
   LOG(LS_INFO) << "Enabling audio level header extension with ID " << id;
-// This api call is not available in iOS version of VoiceEngine currently.
-#if !defined(IOS) && !defined(ANDROID)
   if (engine()->voe()->rtp()->SetRTPAudioLevelIndicationStatus(
       voe_channel(), enable, id) == -1) {
     LOG_RTCERR3(SetRTPAudioLevelIndicationStatus, voe_channel(), enable, id);
     return false;
   }
-#else
-  LOG(LS_WARNING) << "Not enabling audio level header extension with ID " << id
-                  << " because it's not supported on iOS or Android.";
-#endif
 
   return true;
 }
diff --git a/talk/media/webrtc/webrtcvoiceengine_unittest.cc b/talk/media/webrtc/webrtcvoiceengine_unittest.cc
index d13383e..f012930 100644
--- a/talk/media/webrtc/webrtcvoiceengine_unittest.cc
+++ b/talk/media/webrtc/webrtcvoiceengine_unittest.cc
@@ -1170,6 +1170,14 @@
       channel_num, enable, id));
   EXPECT_FALSE(enable);
 
+  // Ensure unknown extentions won't cause an error.
+  extensions.push_back(cricket::RtpHeaderExtension(
+      "urn:ietf:params:unknowextention", 1));
+  EXPECT_TRUE(channel_->SetSendRtpHeaderExtensions(extensions));
+  EXPECT_EQ(0, voe_.GetRTPAudioLevelIndicationStatus(
+      channel_num, enable, id));
+  EXPECT_FALSE(enable);
+
   // Ensure audio levels stay off with an empty list of headers.
   EXPECT_TRUE(channel_->SetSendRtpHeaderExtensions(extensions));
   EXPECT_EQ(0, voe_.GetRTPAudioLevelIndicationStatus(
diff --git a/talk/p2p/base/p2ptransportchannel.cc b/talk/p2p/base/p2ptransportchannel.cc
index cdd3cd9..fbf4ae6 100644
--- a/talk/p2p/base/p2ptransportchannel.cc
+++ b/talk/p2p/base/p2ptransportchannel.cc
@@ -1069,7 +1069,7 @@
     if (remote_ice_mode_ == ICEMODE_FULL && role_ == ROLE_CONTROLLING) {
       use_candidate = (conn == best_connection_) ||
                       (best_connection_ == NULL) ||
-                      (best_connection_->writable()) ||
+                      (!best_connection_->writable()) ||
                       (conn->priority() > best_connection_->priority());
     } else if (remote_ice_mode_ == ICEMODE_LITE && conn == best_connection_) {
       use_candidate = best_connection_->writable();
diff --git a/talk/p2p/base/stun.h b/talk/p2p/base/stun.h
index 8051ca0..6416e51 100644
--- a/talk/p2p/base/stun.h
+++ b/talk/p2p/base/stun.h
@@ -43,7 +43,7 @@
 // These are the types of STUN messages defined in RFC 5389.
 enum StunMessageType {
   STUN_BINDING_REQUEST                  = 0x0001,
-  STUN_BINDING_INDICATION               = 0x0010,
+  STUN_BINDING_INDICATION               = 0x0011,
   STUN_BINDING_RESPONSE                 = 0x0101,
   STUN_BINDING_ERROR_RESPONSE           = 0x0111,
 };
diff --git a/talk/p2p/base/stun_unittest.cc b/talk/p2p/base/stun_unittest.cc
index 93db91a..43db959 100644
--- a/talk/p2p/base/stun_unittest.cc
+++ b/talk/p2p/base/stun_unittest.cc
@@ -565,6 +565,7 @@
     EXPECT_EQ(i == 1, IsStunIndicationType(types[i]));
     EXPECT_EQ(i == 2, IsStunSuccessResponseType(types[i]));
     EXPECT_EQ(i == 3, IsStunErrorResponseType(types[i]));
+    EXPECT_EQ(1, types[i] & 0xFEEF);
   }
 }