Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / libjingle / source / talk / p2p / base / transport.cc
index d177833..95799c7 100644 (file)
@@ -53,6 +53,8 @@ enum {
   MSG_CONNECTING,
   MSG_CANDIDATEALLOCATIONCOMPLETE,
   MSG_ROLECONFLICT,
+  MSG_COMPLETED,
+  MSG_FAILED,
 };
 
 struct ChannelParams : public talk_base::MessageData {
@@ -208,15 +210,14 @@ TransportChannelImpl* Transport::CreateChannel_w(int component) {
   // Push down our transport state to the new channel.
   impl->SetIceRole(ice_role_);
   impl->SetIceTiebreaker(tiebreaker_);
-  if (local_description_) {
-    // TODO(ronghuawu): Change CreateChannel_w to be able to return error since
-    // below Apply**Description_w calls can fail.
+  // TODO(ronghuawu): Change CreateChannel_w to be able to return error since
+  // below Apply**Description_w calls can fail.
+  if (local_description_)
     ApplyLocalTransportDescription_w(impl, NULL);
-    if (remote_description_) {
-      ApplyRemoteTransportDescription_w(impl, NULL);
-      ApplyNegotiatedTransportDescription_w(impl, NULL);
-    }
-  }
+  if (remote_description_)
+    ApplyRemoteTransportDescription_w(impl, NULL);
+  if (local_description_ && remote_description_)
+    ApplyNegotiatedTransportDescription_w(impl, NULL);
 
   impl->SignalReadableState.connect(this, &Transport::OnChannelReadableState);
   impl->SignalWritableState.connect(this, &Transport::OnChannelWritableState);
@@ -227,6 +228,8 @@ TransportChannelImpl* Transport::CreateChannel_w(int component) {
   impl->SignalCandidatesAllocationDone.connect(
       this, &Transport::OnChannelCandidatesAllocationDone);
   impl->SignalRoleConflict.connect(this, &Transport::OnRoleConflict);
+  impl->SignalConnectionRemoved.connect(
+      this, &Transport::OnChannelConnectionRemoved);
 
   if (connect_requested_) {
     impl->Connect();
@@ -621,6 +624,50 @@ void Transport::OnRoleConflict(TransportChannelImpl* channel) {
   signaling_thread_->Post(this, MSG_ROLECONFLICT);
 }
 
+void Transport::OnChannelConnectionRemoved(TransportChannelImpl* channel) {
+  ASSERT(worker_thread()->IsCurrent());
+  // Determine if the Transport should move to Completed or Failed.  These
+  // states are only available in the Controlling ICE role.
+  if (channel->GetIceRole() != ICEROLE_CONTROLLING) {
+    return;
+  }
+
+  ChannelMap::iterator iter = channels_.find(channel->component());
+  ASSERT(iter != channels_.end());
+  // Completed and Failed can only occur after candidate allocation has stopped.
+  if (!iter->second.candidates_allocated()) {
+    return;
+  }
+
+  size_t connections = channel->GetConnectionCount();
+  if (connections == 0) {
+    // A Transport has failed if any of its channels have no remaining
+    // connections.
+    signaling_thread_->Post(this, MSG_FAILED);
+  } else if (connections == 1 && completed()) {
+    signaling_thread_->Post(this, MSG_COMPLETED);
+  }
+}
+
+bool Transport::completed() const {
+  // A Transport's ICE process is completed if all of its channels are writable,
+  // have finished allocating candidates, and have pruned all but one of their
+  // connections.
+  if (!all_channels_writable())
+    return false;
+
+  ChannelMap::const_iterator iter;
+  for (iter = channels_.begin(); iter != channels_.end(); ++iter) {
+    const TransportChannelImpl* channel = iter->second.get();
+    if (!(channel->GetConnectionCount() == 1 &&
+          channel->GetIceRole() == ICEROLE_CONTROLLING &&
+          iter->second.candidates_allocated())) {
+      return false;
+    }
+  }
+  return true;
+}
+
 void Transport::SetIceRole_w(IceRole role) {
   talk_base::CritScope cs(&crit_);
   ice_role_ = role;
@@ -684,6 +731,21 @@ bool Transport::SetRemoteTransportDescription_w(
 
 bool Transport::ApplyLocalTransportDescription_w(TransportChannelImpl* ch,
                                                  std::string* error_desc) {
+  // If existing protocol_type is HYBRID, we may have not chosen the final
+  // protocol type, so update the channel protocol type from the
+  // local description. Otherwise, skip updating the protocol type.
+  // We check for HYBRID to avoid accidental changes; in the case of a
+  // session renegotiation, the new offer will have the google-ice ICE option,
+  // so we need to make sure we don't switch back from ICE mode to HYBRID
+  // when this happens.
+  // There are some other ways we could have solved this, but this is the
+  // simplest. The ultimate solution will be to get rid of GICE altogether.
+  IceProtocolType protocol_type;
+  if (ch->GetIceProtocolType(&protocol_type) &&
+      protocol_type == ICEPROTO_HYBRID) {
+    ch->SetIceProtocolType(
+        TransportProtocolFromDescription(local_description()));
+  }
   ch->SetIceCredentials(local_description_->ice_ufrag,
                         local_description_->ice_pwd);
   return true;
@@ -806,6 +868,12 @@ void Transport::OnMessage(talk_base::Message* msg) {
     case MSG_ROLECONFLICT:
       SignalRoleConflict();
       break;
+    case MSG_COMPLETED:
+      SignalCompleted(this);
+      break;
+    case MSG_FAILED:
+      SignalFailed(this);
+      break;
   }
 }