MSG_CONNECTING,
MSG_CANDIDATEALLOCATIONCOMPLETE,
MSG_ROLECONFLICT,
+ MSG_COMPLETED,
+ MSG_FAILED,
};
struct ChannelParams : public talk_base::MessageData {
// 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);
impl->SignalCandidatesAllocationDone.connect(
this, &Transport::OnChannelCandidatesAllocationDone);
impl->SignalRoleConflict.connect(this, &Transport::OnRoleConflict);
+ impl->SignalConnectionRemoved.connect(
+ this, &Transport::OnChannelConnectionRemoved);
if (connect_requested_) {
impl->Connect();
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;
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;
case MSG_ROLECONFLICT:
SignalRoleConflict();
break;
+ case MSG_COMPLETED:
+ SignalCompleted(this);
+ break;
+ case MSG_FAILED:
+ SignalFailed(this);
+ break;
}
}