webrtc_m130/webrtc/p2p/base/session.cc
decurtis@webrtc.org 357469da5a Fixes reference counting problem when a TransportProxy points to a Transport prior to creating channels.
Until the TransportProxy enters the "negotiated" state we only create
ChannelImpls but we don't hook up to them. However, we still neeed to
reserve their spot and increment the reference count.

Once we are negotiated we can hook all the ChannelProxy's to the
corresponding ChannelImpls.

This change is needed to implement maxbundle.

BUG=1574
R=pthatcher@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@8082 4adac7df-926f-26a2-2b94-8c16560cd09d
2015-01-15 22:53:49 +00:00

913 lines
30 KiB
C++

/*
* Copyright 2004 The WebRTC Project Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/p2p/base/session.h"
#include "webrtc/p2p/base/dtlstransport.h"
#include "webrtc/p2p/base/p2ptransport.h"
#include "webrtc/p2p/base/transport.h"
#include "webrtc/p2p/base/transportchannelproxy.h"
#include "webrtc/p2p/base/transportinfo.h"
#include "webrtc/base/bind.h"
#include "webrtc/base/common.h"
#include "webrtc/base/helpers.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/base/stringencode.h"
#include "webrtc/base/sslstreamadapter.h"
#include "webrtc/p2p/base/constants.h"
namespace cricket {
using rtc::Bind;
TransportProxy::~TransportProxy() {
for (ChannelMap::iterator iter = channels_.begin();
iter != channels_.end(); ++iter) {
iter->second->SignalDestroyed(iter->second);
delete iter->second;
}
}
const std::string& TransportProxy::type() const {
return transport_->get()->type();
}
TransportChannel* TransportProxy::GetChannel(int component) {
ASSERT(rtc::Thread::Current() == worker_thread_);
return GetChannelProxy(component);
}
TransportChannel* TransportProxy::CreateChannel(const std::string& name,
int component) {
ASSERT(rtc::Thread::Current() == worker_thread_);
ASSERT(GetChannel(component) == NULL);
ASSERT(!transport_->get()->HasChannel(component));
// We always create a proxy in case we need to change out the transport later.
TransportChannelProxy* channel_proxy =
new TransportChannelProxy(content_name(), name, component);
channels_[component] = channel_proxy;
// If we're already negotiated, create an impl and hook it up to the proxy
// channel. If we're connecting, create an impl but don't hook it up yet.
if (negotiated_) {
CreateChannelImpl_w(component);
SetChannelImplFromTransport_w(channel_proxy, component);
} else if (connecting_) {
CreateChannelImpl_w(component);
}
return channel_proxy;
}
bool TransportProxy::HasChannel(int component) {
return transport_->get()->HasChannel(component);
}
void TransportProxy::DestroyChannel(int component) {
ASSERT(rtc::Thread::Current() == worker_thread_);
TransportChannelProxy* channel_proxy = GetChannelProxy(component);
if (channel_proxy) {
// If the state of TransportProxy is not NEGOTIATED then
// TransportChannelProxy and its impl are not connected. Both must
// be connected before deletion.
//
// However, if we haven't entered the connecting state then there
// is no implementation to hook up.
if (connecting_ && !negotiated_) {
SetChannelImplFromTransport_w(channel_proxy, component);
}
channels_.erase(component);
channel_proxy->SignalDestroyed(channel_proxy);
delete channel_proxy;
}
}
void TransportProxy::ConnectChannels() {
if (!connecting_) {
if (!negotiated_) {
for (auto& iter : channels_) {
CreateChannelImpl(iter.first);
}
}
connecting_ = true;
}
// TODO(juberti): Right now Transport::ConnectChannels doesn't work if we
// don't have any channels yet, so we need to allow this method to be called
// multiple times. Once we fix Transport, we can move this call inside the
// if (!connecting_) block.
transport_->get()->ConnectChannels();
}
void TransportProxy::CompleteNegotiation() {
if (!negotiated_) {
// Negotiating assumes connecting_ has happened and
// implementations exist. If not we need to create the
// implementations.
for (auto& iter : channels_) {
if (!connecting_) {
CreateChannelImpl(iter.first);
}
SetChannelImplFromTransport(iter.second, iter.first);
}
negotiated_ = true;
}
}
void TransportProxy::AddSentCandidates(const Candidates& candidates) {
for (Candidates::const_iterator cand = candidates.begin();
cand != candidates.end(); ++cand) {
sent_candidates_.push_back(*cand);
}
}
void TransportProxy::AddUnsentCandidates(const Candidates& candidates) {
for (Candidates::const_iterator cand = candidates.begin();
cand != candidates.end(); ++cand) {
unsent_candidates_.push_back(*cand);
}
}
bool TransportProxy::GetChannelNameFromComponent(
int component, std::string* channel_name) const {
const TransportChannelProxy* channel = GetChannelProxy(component);
if (channel == NULL) {
return false;
}
*channel_name = channel->name();
return true;
}
bool TransportProxy::GetComponentFromChannelName(
const std::string& channel_name, int* component) const {
const TransportChannelProxy* channel = GetChannelProxyByName(channel_name);
if (channel == NULL) {
return false;
}
*component = channel->component();
return true;
}
TransportChannelProxy* TransportProxy::GetChannelProxy(int component) const {
ChannelMap::const_iterator iter = channels_.find(component);
return (iter != channels_.end()) ? iter->second : NULL;
}
TransportChannelProxy* TransportProxy::GetChannelProxyByName(
const std::string& name) const {
for (ChannelMap::const_iterator iter = channels_.begin();
iter != channels_.end();
++iter) {
if (iter->second->name() == name) {
return iter->second;
}
}
return NULL;
}
void TransportProxy::CreateChannelImpl(int component) {
worker_thread_->Invoke<void>(Bind(
&TransportProxy::CreateChannelImpl_w, this, component));
}
void TransportProxy::CreateChannelImpl_w(int component) {
ASSERT(rtc::Thread::Current() == worker_thread_);
transport_->get()->CreateChannel(component);
}
void TransportProxy::SetChannelImplFromTransport(TransportChannelProxy* proxy,
int component) {
worker_thread_->Invoke<void>(Bind(
&TransportProxy::SetChannelImplFromTransport_w, this, proxy, component));
}
void TransportProxy::SetChannelImplFromTransport_w(TransportChannelProxy* proxy,
int component) {
ASSERT(rtc::Thread::Current() == worker_thread_);
TransportChannelImpl* impl = transport_->get()->GetChannel(component);
ASSERT(impl != NULL);
ReplaceChannelImpl_w(proxy, impl);
}
void TransportProxy::ReplaceChannelImpl(TransportChannelProxy* proxy,
TransportChannelImpl* impl) {
worker_thread_->Invoke<void>(Bind(
&TransportProxy::ReplaceChannelImpl_w, this, proxy, impl));
}
void TransportProxy::ReplaceChannelImpl_w(TransportChannelProxy* proxy,
TransportChannelImpl* impl) {
ASSERT(rtc::Thread::Current() == worker_thread_);
ASSERT(proxy != NULL);
proxy->SetImplementation(impl);
}
// This function muxes |this| onto |target| by repointing |this| at
// |target|'s transport and setting our TransportChannelProxies
// to point to |target|'s underlying implementations.
bool TransportProxy::SetupMux(TransportProxy* target) {
// Bail out if there's nothing to do.
if (transport_ == target->transport_) {
return true;
}
// Run through all channels and remove any non-rtp transport channels before
// setting target transport channels.
for (ChannelMap::const_iterator iter = channels_.begin();
iter != channels_.end(); ++iter) {
if (!target->transport_->get()->HasChannel(iter->first)) {
// Remove if channel doesn't exist in |transport_|.
ReplaceChannelImpl(iter->second, NULL);
} else {
// Replace the impl for all the TransportProxyChannels with the channels
// from |target|'s transport. Fail if there's not an exact match.
ReplaceChannelImpl(
iter->second, target->transport_->get()->CreateChannel(iter->first));
}
}
// Now replace our transport. Must happen afterwards because
// it deletes all impls as a side effect.
transport_ = target->transport_;
transport_->get()->SignalCandidatesReady.connect(
this, &TransportProxy::OnTransportCandidatesReady);
set_candidates_allocated(target->candidates_allocated());
return true;
}
void TransportProxy::SetIceRole(IceRole role) {
transport_->get()->SetIceRole(role);
}
bool TransportProxy::SetLocalTransportDescription(
const TransportDescription& description,
ContentAction action,
std::string* error_desc) {
// If this is an answer, finalize the negotiation.
if (action == CA_ANSWER) {
CompleteNegotiation();
}
bool result = transport_->get()->SetLocalTransportDescription(description,
action,
error_desc);
if (result)
local_description_set_ = true;
return result;
}
bool TransportProxy::SetRemoteTransportDescription(
const TransportDescription& description,
ContentAction action,
std::string* error_desc) {
// If this is an answer, finalize the negotiation.
if (action == CA_ANSWER) {
CompleteNegotiation();
}
bool result = transport_->get()->SetRemoteTransportDescription(description,
action,
error_desc);
if (result)
remote_description_set_ = true;
return result;
}
void TransportProxy::OnSignalingReady() {
// If we're starting a new allocation sequence, reset our state.
set_candidates_allocated(false);
transport_->get()->OnSignalingReady();
}
bool TransportProxy::OnRemoteCandidates(const Candidates& candidates,
std::string* error) {
// Ensure the transport is negotiated before handling candidates.
// TODO(juberti): Remove this once everybody calls SetLocalTD.
CompleteNegotiation();
// Verify each candidate before passing down to transport layer.
for (Candidates::const_iterator cand = candidates.begin();
cand != candidates.end(); ++cand) {
if (!transport_->get()->VerifyCandidate(*cand, error))
return false;
if (!HasChannel(cand->component())) {
*error = "Candidate has unknown component: " + cand->ToString() +
" for content: " + content_name_;
return false;
}
}
transport_->get()->OnRemoteCandidates(candidates);
return true;
}
void TransportProxy::SetIdentity(
rtc::SSLIdentity* identity) {
transport_->get()->SetIdentity(identity);
}
std::string BaseSession::StateToString(State state) {
switch (state) {
case STATE_INIT:
return "STATE_INIT";
case STATE_SENTINITIATE:
return "STATE_SENTINITIATE";
case STATE_RECEIVEDINITIATE:
return "STATE_RECEIVEDINITIATE";
case STATE_SENTPRACCEPT:
return "STATE_SENTPRACCEPT";
case STATE_SENTACCEPT:
return "STATE_SENTACCEPT";
case STATE_RECEIVEDPRACCEPT:
return "STATE_RECEIVEDPRACCEPT";
case STATE_RECEIVEDACCEPT:
return "STATE_RECEIVEDACCEPT";
case STATE_SENTMODIFY:
return "STATE_SENTMODIFY";
case STATE_RECEIVEDMODIFY:
return "STATE_RECEIVEDMODIFY";
case STATE_SENTREJECT:
return "STATE_SENTREJECT";
case STATE_RECEIVEDREJECT:
return "STATE_RECEIVEDREJECT";
case STATE_SENTREDIRECT:
return "STATE_SENTREDIRECT";
case STATE_SENTTERMINATE:
return "STATE_SENTTERMINATE";
case STATE_RECEIVEDTERMINATE:
return "STATE_RECEIVEDTERMINATE";
case STATE_INPROGRESS:
return "STATE_INPROGRESS";
case STATE_DEINIT:
return "STATE_DEINIT";
default:
break;
}
return "STATE_" + rtc::ToString(state);
}
BaseSession::BaseSession(rtc::Thread* signaling_thread,
rtc::Thread* worker_thread,
PortAllocator* port_allocator,
const std::string& sid,
const std::string& content_type,
bool initiator)
: state_(STATE_INIT),
error_(ERROR_NONE),
signaling_thread_(signaling_thread),
worker_thread_(worker_thread),
port_allocator_(port_allocator),
sid_(sid),
content_type_(content_type),
transport_type_(NS_GINGLE_P2P),
initiator_(initiator),
identity_(NULL),
ice_tiebreaker_(rtc::CreateRandomId64()),
role_switch_(false) {
ASSERT(signaling_thread->IsCurrent());
}
BaseSession::~BaseSession() {
ASSERT(signaling_thread()->IsCurrent());
ASSERT(state_ != STATE_DEINIT);
LogState(state_, STATE_DEINIT);
state_ = STATE_DEINIT;
SignalState(this, state_);
for (TransportMap::iterator iter = transports_.begin();
iter != transports_.end(); ++iter) {
delete iter->second;
}
}
const SessionDescription* BaseSession::local_description() const {
// TODO(tommi): Assert on thread correctness.
return local_description_.get();
}
const SessionDescription* BaseSession::remote_description() const {
// TODO(tommi): Assert on thread correctness.
return remote_description_.get();
}
SessionDescription* BaseSession::remote_description() {
// TODO(tommi): Assert on thread correctness.
return remote_description_.get();
}
void BaseSession::set_local_description(const SessionDescription* sdesc) {
// TODO(tommi): Assert on thread correctness.
if (sdesc != local_description_.get())
local_description_.reset(sdesc);
}
void BaseSession::set_remote_description(SessionDescription* sdesc) {
// TODO(tommi): Assert on thread correctness.
if (sdesc != remote_description_)
remote_description_.reset(sdesc);
}
const SessionDescription* BaseSession::initiator_description() const {
// TODO(tommi): Assert on thread correctness.
return initiator_ ? local_description_.get() : remote_description_.get();
}
bool BaseSession::SetIdentity(rtc::SSLIdentity* identity) {
if (identity_)
return false;
identity_ = identity;
for (TransportMap::iterator iter = transports_.begin();
iter != transports_.end(); ++iter) {
iter->second->SetIdentity(identity_);
}
return true;
}
bool BaseSession::PushdownTransportDescription(ContentSource source,
ContentAction action,
std::string* error_desc) {
if (source == CS_LOCAL) {
return PushdownLocalTransportDescription(local_description(),
action,
error_desc);
}
return PushdownRemoteTransportDescription(remote_description(),
action,
error_desc);
}
bool BaseSession::PushdownLocalTransportDescription(
const SessionDescription* sdesc,
ContentAction action,
std::string* error_desc) {
// Update the Transports with the right information, and trigger them to
// start connecting.
for (TransportMap::iterator iter = transports_.begin();
iter != transports_.end(); ++iter) {
// If no transport info was in this session description, ret == false
// and we just skip this one.
TransportDescription tdesc;
bool ret = GetTransportDescription(
sdesc, iter->second->content_name(), &tdesc);
if (ret) {
if (!iter->second->SetLocalTransportDescription(tdesc, action,
error_desc)) {
return false;
}
iter->second->ConnectChannels();
}
}
return true;
}
bool BaseSession::PushdownRemoteTransportDescription(
const SessionDescription* sdesc,
ContentAction action,
std::string* error_desc) {
// Update the Transports with the right information.
for (TransportMap::iterator iter = transports_.begin();
iter != transports_.end(); ++iter) {
TransportDescription tdesc;
// If no transport info was in this session description, ret == false
// and we just skip this one.
bool ret = GetTransportDescription(
sdesc, iter->second->content_name(), &tdesc);
if (ret) {
if (!iter->second->SetRemoteTransportDescription(tdesc, action,
error_desc)) {
return false;
}
}
}
return true;
}
TransportChannel* BaseSession::CreateChannel(const std::string& content_name,
const std::string& channel_name,
int component) {
// We create the proxy "on demand" here because we need to support
// creating channels at any time, even before we send or receive
// initiate messages, which is before we create the transports.
TransportProxy* transproxy = GetOrCreateTransportProxy(content_name);
return transproxy->CreateChannel(channel_name, component);
}
TransportChannel* BaseSession::GetChannel(const std::string& content_name,
int component) {
TransportProxy* transproxy = GetTransportProxy(content_name);
if (transproxy == NULL)
return NULL;
return transproxy->GetChannel(component);
}
void BaseSession::DestroyChannel(const std::string& content_name,
int component) {
TransportProxy* transproxy = GetTransportProxy(content_name);
ASSERT(transproxy != NULL);
transproxy->DestroyChannel(component);
}
TransportProxy* BaseSession::GetOrCreateTransportProxy(
const std::string& content_name) {
TransportProxy* transproxy = GetTransportProxy(content_name);
if (transproxy)
return transproxy;
Transport* transport = CreateTransport(content_name);
transport->SetIceRole(initiator_ ? ICEROLE_CONTROLLING : ICEROLE_CONTROLLED);
transport->SetIceTiebreaker(ice_tiebreaker_);
// TODO: Connect all the Transport signals to TransportProxy
// then to the BaseSession.
transport->SignalConnecting.connect(
this, &BaseSession::OnTransportConnecting);
transport->SignalWritableState.connect(
this, &BaseSession::OnTransportWritable);
transport->SignalRequestSignaling.connect(
this, &BaseSession::OnTransportRequestSignaling);
transport->SignalRouteChange.connect(
this, &BaseSession::OnTransportRouteChange);
transport->SignalCandidatesAllocationDone.connect(
this, &BaseSession::OnTransportCandidatesAllocationDone);
transport->SignalRoleConflict.connect(
this, &BaseSession::OnRoleConflict);
transport->SignalCompleted.connect(
this, &BaseSession::OnTransportCompleted);
transport->SignalFailed.connect(
this, &BaseSession::OnTransportFailed);
transproxy = new TransportProxy(worker_thread_, sid_, content_name,
new TransportWrapper(transport));
transproxy->SignalCandidatesReady.connect(
this, &BaseSession::OnTransportProxyCandidatesReady);
if (identity_)
transproxy->SetIdentity(identity_);
transports_[content_name] = transproxy;
return transproxy;
}
Transport* BaseSession::GetTransport(const std::string& content_name) {
TransportProxy* transproxy = GetTransportProxy(content_name);
if (transproxy == NULL)
return NULL;
return transproxy->impl();
}
TransportProxy* BaseSession::GetTransportProxy(
const std::string& content_name) {
TransportMap::iterator iter = transports_.find(content_name);
return (iter != transports_.end()) ? iter->second : NULL;
}
TransportProxy* BaseSession::GetTransportProxy(const Transport* transport) {
for (TransportMap::iterator iter = transports_.begin();
iter != transports_.end(); ++iter) {
TransportProxy* transproxy = iter->second;
if (transproxy->impl() == transport) {
return transproxy;
}
}
return NULL;
}
TransportProxy* BaseSession::GetFirstTransportProxy() {
if (transports_.empty())
return NULL;
return transports_.begin()->second;
}
void BaseSession::DestroyTransportProxy(
const std::string& content_name) {
TransportMap::iterator iter = transports_.find(content_name);
if (iter != transports_.end()) {
delete iter->second;
transports_.erase(content_name);
}
}
cricket::Transport* BaseSession::CreateTransport(
const std::string& content_name) {
ASSERT(transport_type_ == NS_GINGLE_P2P);
return new cricket::DtlsTransport<P2PTransport>(
signaling_thread(), worker_thread(), content_name,
port_allocator(), identity_);
}
bool BaseSession::GetStats(SessionStats* stats) {
for (TransportMap::iterator iter = transports_.begin();
iter != transports_.end(); ++iter) {
std::string proxy_id = iter->second->content_name();
// We are ignoring not-yet-instantiated transports.
if (iter->second->impl()) {
std::string transport_id = iter->second->impl()->content_name();
stats->proxy_to_transport[proxy_id] = transport_id;
if (stats->transport_stats.find(transport_id)
== stats->transport_stats.end()) {
TransportStats subinfos;
if (!iter->second->impl()->GetStats(&subinfos)) {
return false;
}
stats->transport_stats[transport_id] = subinfos;
}
}
}
return true;
}
void BaseSession::SetState(State state) {
ASSERT(signaling_thread_->IsCurrent());
if (state != state_) {
LogState(state_, state);
state_ = state;
SignalState(this, state_);
signaling_thread_->Post(this, MSG_STATE);
}
SignalNewDescription();
}
void BaseSession::SetError(Error error, const std::string& error_desc) {
ASSERT(signaling_thread_->IsCurrent());
if (error != error_) {
error_ = error;
error_desc_ = error_desc;
SignalError(this, error);
}
}
void BaseSession::OnSignalingReady() {
ASSERT(signaling_thread()->IsCurrent());
for (TransportMap::iterator iter = transports_.begin();
iter != transports_.end(); ++iter) {
iter->second->OnSignalingReady();
}
}
// TODO(juberti): Since PushdownLocalTD now triggers the connection process to
// start, remove this method once everyone calls PushdownLocalTD.
void BaseSession::SpeculativelyConnectAllTransportChannels() {
// Put all transports into the connecting state.
for (TransportMap::iterator iter = transports_.begin();
iter != transports_.end(); ++iter) {
iter->second->ConnectChannels();
}
}
bool BaseSession::OnRemoteCandidates(const std::string& content_name,
const Candidates& candidates,
std::string* error) {
// Give candidates to the appropriate transport, and tell that transport
// to start connecting, if it's not already doing so.
TransportProxy* transproxy = GetTransportProxy(content_name);
if (!transproxy) {
*error = "Unknown content name " + content_name;
return false;
}
if (!transproxy->OnRemoteCandidates(candidates, error)) {
return false;
}
// TODO(juberti): Remove this call once we can be sure that we always have
// a local transport description (which will trigger the connection).
transproxy->ConnectChannels();
return true;
}
bool BaseSession::MaybeEnableMuxingSupport() {
// We need both a local and remote description to decide if we should mux.
if ((state_ == STATE_SENTINITIATE ||
state_ == STATE_RECEIVEDINITIATE) &&
((local_description_ == NULL) ||
(remote_description_ == NULL))) {
return false;
}
// In order to perform the multiplexing, we need all proxies to be in the
// negotiated state, i.e. to have implementations underneath.
// Ensure that this is the case, regardless of whether we are going to mux.
for (TransportMap::iterator iter = transports_.begin();
iter != transports_.end(); ++iter) {
ASSERT(iter->second->negotiated());
if (!iter->second->negotiated())
return false;
}
// If both sides agree to BUNDLE, mux all the specified contents onto the
// transport belonging to the first content name in the BUNDLE group.
// If the contents are already muxed, this will be a no-op.
// TODO(juberti): Should this check that local and remote have configured
// BUNDLE the same way?
bool candidates_allocated = IsCandidateAllocationDone();
const ContentGroup* local_bundle_group =
local_description()->GetGroupByName(GROUP_TYPE_BUNDLE);
const ContentGroup* remote_bundle_group =
remote_description()->GetGroupByName(GROUP_TYPE_BUNDLE);
if (local_bundle_group && remote_bundle_group &&
local_bundle_group->FirstContentName()) {
const std::string* content_name = local_bundle_group->FirstContentName();
const ContentInfo* content =
local_description_->GetContentByName(*content_name);
if (!content) {
LOG(LS_WARNING) << "Content \"" << *content_name
<< "\" referenced in BUNDLE group is not present";
return false;
}
if (!SetSelectedProxy(content->name, local_bundle_group)) {
LOG(LS_WARNING) << "Failed to set up BUNDLE";
return false;
}
// If we weren't done gathering before, we might be done now, as a result
// of enabling mux.
LOG(LS_INFO) << "Enabling BUNDLE, bundling onto transport: "
<< *content_name;
if (!candidates_allocated) {
MaybeCandidateAllocationDone();
}
} else {
LOG(LS_INFO) << "No BUNDLE information, not bundling.";
}
return true;
}
bool BaseSession::SetSelectedProxy(const std::string& content_name,
const ContentGroup* muxed_group) {
TransportProxy* selected_proxy = GetTransportProxy(content_name);
if (!selected_proxy) {
return false;
}
ASSERT(selected_proxy->negotiated());
for (TransportMap::iterator iter = transports_.begin();
iter != transports_.end(); ++iter) {
// If content is part of the mux group, then repoint its proxy at the
// transport object that we have chosen to mux onto. If the proxy
// is already pointing at the right object, it will be a no-op.
if (muxed_group->HasContentName(iter->first) &&
!iter->second->SetupMux(selected_proxy)) {
return false;
}
}
return true;
}
void BaseSession::OnTransportCandidatesAllocationDone(Transport* transport) {
// TODO(juberti): This is a clunky way of processing the done signal. Instead,
// TransportProxy should receive the done signal directly, set its allocated
// flag internally, and then reissue the done signal to Session.
// Overall we should make TransportProxy receive *all* the signals from
// Transport, since this removes the need to manually iterate over all
// the transports, as is needed to make sure signals are handled properly
// when BUNDLEing.
// TODO(juberti): Per b/7998978, devs and QA are hitting this assert in ways
// that make it prohibitively difficult to run dbg builds. Disabled for now.
//ASSERT(!IsCandidateAllocationDone());
for (TransportMap::iterator iter = transports_.begin();
iter != transports_.end(); ++iter) {
if (iter->second->impl() == transport) {
iter->second->set_candidates_allocated(true);
}
}
MaybeCandidateAllocationDone();
}
bool BaseSession::IsCandidateAllocationDone() const {
for (TransportMap::const_iterator iter = transports_.begin();
iter != transports_.end(); ++iter) {
if (!iter->second->candidates_allocated())
return false;
}
return true;
}
void BaseSession::MaybeCandidateAllocationDone() {
if (IsCandidateAllocationDone()) {
LOG(LS_INFO) << "Candidate gathering is complete.";
OnCandidatesAllocationDone();
}
}
void BaseSession::OnRoleConflict() {
if (role_switch_) {
LOG(LS_WARNING) << "Repeat of role conflict signal from Transport.";
return;
}
role_switch_ = true;
for (TransportMap::iterator iter = transports_.begin();
iter != transports_.end(); ++iter) {
// Role will be reverse of initial role setting.
IceRole role = initiator_ ? ICEROLE_CONTROLLED : ICEROLE_CONTROLLING;
iter->second->SetIceRole(role);
}
}
void BaseSession::LogState(State old_state, State new_state) {
LOG(LS_INFO) << "Session:" << id()
<< " Old state:" << StateToString(old_state)
<< " New state:" << StateToString(new_state)
<< " Type:" << content_type()
<< " Transport:" << transport_type();
}
// static
bool BaseSession::GetTransportDescription(const SessionDescription* description,
const std::string& content_name,
TransportDescription* tdesc) {
if (!description || !tdesc) {
return false;
}
const TransportInfo* transport_info =
description->GetTransportInfoByName(content_name);
if (!transport_info) {
return false;
}
*tdesc = transport_info->description;
return true;
}
void BaseSession::SignalNewDescription() {
ContentAction action;
ContentSource source;
if (!GetContentAction(&action, &source)) {
return;
}
if (source == CS_LOCAL) {
SignalNewLocalDescription(this, action);
} else {
SignalNewRemoteDescription(this, action);
}
}
bool BaseSession::GetContentAction(ContentAction* action,
ContentSource* source) {
switch (state_) {
// new local description
case STATE_SENTINITIATE:
*action = CA_OFFER;
*source = CS_LOCAL;
break;
case STATE_SENTPRACCEPT:
*action = CA_PRANSWER;
*source = CS_LOCAL;
break;
case STATE_SENTACCEPT:
*action = CA_ANSWER;
*source = CS_LOCAL;
break;
// new remote description
case STATE_RECEIVEDINITIATE:
*action = CA_OFFER;
*source = CS_REMOTE;
break;
case STATE_RECEIVEDPRACCEPT:
*action = CA_PRANSWER;
*source = CS_REMOTE;
break;
case STATE_RECEIVEDACCEPT:
*action = CA_ANSWER;
*source = CS_REMOTE;
break;
default:
return false;
}
return true;
}
void BaseSession::OnMessage(rtc::Message *pmsg) {
switch (pmsg->message_id) {
case MSG_TIMEOUT:
// Session timeout has occured.
SetError(ERROR_TIME, "Session timeout has occured.");
break;
case MSG_STATE:
switch (state_) {
case STATE_SENTACCEPT:
case STATE_RECEIVEDACCEPT:
SetState(STATE_INPROGRESS);
break;
default:
// Explicitly ignoring some states here.
break;
}
break;
}
}
} // namespace cricket