summaryrefslogtreecommitdiffstats
path: root/remoting/protocol/jingle_session_manager.cc
diff options
context:
space:
mode:
authorgarykac@chromium.org <garykac@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-03 21:56:42 +0000
committergarykac@chromium.org <garykac@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-03 21:56:42 +0000
commit230d2fd2e1cafaab5cd5e4d2a9f64b4ae272550a (patch)
tree8aa02e4a85f0d314a4419a434037e163a41981c8 /remoting/protocol/jingle_session_manager.cc
parent3296839602c5c54cae0b0fb0a9d6623a5ba65895 (diff)
downloadchromium_src-230d2fd2e1cafaab5cd5e4d2a9f64b4ae272550a.zip
chromium_src-230d2fd2e1cafaab5cd5e4d2a9f64b4ae272550a.tar.gz
chromium_src-230d2fd2e1cafaab5cd5e4d2a9f64b4ae272550a.tar.bz2
Rename classes for Chromoting:
ChromotocolServer -> protocol::SessionManager ChromotocolConnection -> protocol::Session BUG=none TEST=compiles + make chromoting connection Review URL: http://codereview.chromium.org/4313001 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@64971 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/protocol/jingle_session_manager.cc')
-rw-r--r--remoting/protocol/jingle_session_manager.cc478
1 files changed, 478 insertions, 0 deletions
diff --git a/remoting/protocol/jingle_session_manager.cc b/remoting/protocol/jingle_session_manager.cc
new file mode 100644
index 0000000..8784f83
--- /dev/null
+++ b/remoting/protocol/jingle_session_manager.cc
@@ -0,0 +1,478 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "remoting/protocol/jingle_session_manager.h"
+
+#include "base/message_loop.h"
+#include "base/string_number_conversions.h"
+#include "remoting/base/constants.h"
+#include "remoting/jingle_glue/jingle_thread.h"
+#include "third_party/libjingle/source/talk/p2p/base/constants.h"
+#include "third_party/libjingle/source/talk/p2p/base/transport.h"
+#include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
+
+using cricket::ContentDescription;
+using cricket::SessionDescription;
+using buzz::QName;
+using buzz::XmlElement;
+
+namespace remoting {
+
+namespace protocol {
+
+namespace {
+
+const char kDefaultNs[] = "";
+
+const char kChromotingContentName[] = "chromoting";
+
+// Following constants are used to format session description in XML.
+const char kDescriptionTag[] = "description";
+const char kControlTag[] = "control";
+const char kEventTag[] = "event";
+const char kVideoTag[] = "video";
+const char kResolutionTag[] = "initial-resolution";
+
+const char kTransportAttr[] = "transport";
+const char kVersionAttr[] = "version";
+const char kCodecAttr[] = "codec";
+const char kWidthAttr[] = "width";
+const char kHeightAttr[] = "height";
+
+const char kStreamTransport[] = "stream";
+const char kDatagramTransport[] = "datagram";
+const char kSrtpTransport[] = "srtp";
+const char kRtpDtlsTransport[] = "rtp-dtls";
+
+const char kVp8Codec[] = "vp8";
+const char kZipCodec[] = "zip";
+
+const char* GetTransportName(ChannelConfig::TransportType type) {
+ switch (type) {
+ case ChannelConfig::TRANSPORT_STREAM:
+ return kStreamTransport;
+ case ChannelConfig::TRANSPORT_DATAGRAM:
+ return kDatagramTransport;
+ case ChannelConfig::TRANSPORT_SRTP:
+ return kSrtpTransport;
+ case ChannelConfig::TRANSPORT_RTP_DTLS:
+ return kRtpDtlsTransport;
+ }
+ NOTREACHED();
+ return NULL;
+}
+
+const char* GetCodecName(ChannelConfig::Codec type) {
+ switch (type) {
+ case ChannelConfig::CODEC_VP8:
+ return kVp8Codec;
+ case ChannelConfig::CODEC_ZIP:
+ return kZipCodec;
+ default:
+ break;
+ }
+ NOTREACHED();
+ return NULL;
+}
+
+
+// Format a channel configuration tag for chromotocol session description,
+// e.g. for video channel:
+// <video transport="srtp" version="1" codec="vp8" />
+XmlElement* FormatChannelConfig(const ChannelConfig& config,
+ const std::string& tag_name) {
+ XmlElement* result = new XmlElement(
+ QName(kChromotingXmlNamespace, tag_name));
+
+ result->AddAttr(QName(kDefaultNs, kTransportAttr),
+ GetTransportName(config.transport));
+
+ result->AddAttr(QName(kDefaultNs, kVersionAttr),
+ base::IntToString(config.version));
+
+ if (config.codec != ChannelConfig::CODEC_UNDEFINED) {
+ result->AddAttr(QName(kDefaultNs, kCodecAttr),
+ GetCodecName(config.codec));
+ }
+
+ return result;
+}
+
+bool ParseTransportName(const std::string& value,
+ ChannelConfig::TransportType* transport) {
+ if (value == kStreamTransport) {
+ *transport = ChannelConfig::TRANSPORT_STREAM;
+ } else if (value == kDatagramTransport) {
+ *transport = ChannelConfig::TRANSPORT_DATAGRAM;
+ } else if (value == kSrtpTransport) {
+ *transport = ChannelConfig::TRANSPORT_SRTP;
+ } else if (value == kRtpDtlsTransport) {
+ *transport = ChannelConfig::TRANSPORT_RTP_DTLS;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+bool ParseCodecName(const std::string& value, ChannelConfig::Codec* codec) {
+ if (value == kVp8Codec) {
+ *codec = ChannelConfig::CODEC_VP8;
+ } else if (value == kZipCodec) {
+ *codec = ChannelConfig::CODEC_ZIP;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+// Returns false if the element is invalid.
+bool ParseChannelConfig(const XmlElement* element, bool codec_required,
+ ChannelConfig* config) {
+ if (!ParseTransportName(element->Attr(QName(kDefaultNs, kTransportAttr)),
+ &config->transport) ||
+ !base::StringToInt(element->Attr(QName(kDefaultNs, kVersionAttr)),
+ &config->version)) {
+ return false;
+ }
+
+ if (codec_required) {
+ if (!ParseCodecName(element->Attr(QName(kDefaultNs, kCodecAttr)),
+ &config->codec)) {
+ return false;
+ }
+ } else {
+ config->codec = ChannelConfig::CODEC_UNDEFINED;
+ }
+
+ return true;
+}
+
+} // namespace
+
+ContentDescription::ContentDescription(
+ const CandidateChromotocolConfig* config)
+ : candidate_config_(config) {
+}
+
+ContentDescription::~ContentDescription() { }
+
+JingleSessionManager::JingleSessionManager(
+ JingleThread* jingle_thread)
+ : jingle_thread_(jingle_thread),
+ cricket_session_manager_(NULL),
+ allow_local_ips_(false),
+ closed_(false) {
+ DCHECK(jingle_thread_);
+}
+
+void JingleSessionManager::Init(
+ const std::string& local_jid,
+ cricket::SessionManager* cricket_session_manager,
+ IncomingSessionCallback* incoming_session_callback) {
+ if (MessageLoop::current() != message_loop()) {
+ message_loop()->PostTask(
+ FROM_HERE, NewRunnableMethod(
+ this, &JingleSessionManager::Init,
+ local_jid, cricket_session_manager, incoming_session_callback));
+ return;
+ }
+
+ DCHECK(cricket_session_manager);
+ DCHECK(incoming_session_callback);
+
+ local_jid_ = local_jid;
+ incoming_session_callback_.reset(incoming_session_callback);
+ cricket_session_manager_ = cricket_session_manager;
+ cricket_session_manager_->AddClient(kChromotingXmlNamespace, this);
+}
+
+JingleSessionManager::~JingleSessionManager() {
+ DCHECK(closed_);
+}
+
+void JingleSessionManager::Close(Task* closed_task) {
+ if (MessageLoop::current() != message_loop()) {
+ message_loop()->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &JingleSessionManager::Close,
+ closed_task));
+ return;
+ }
+
+ if (!closed_) {
+ // Close all connections.
+ cricket_session_manager_->RemoveClient(kChromotingXmlNamespace);
+ while (!sessions_.empty()) {
+ cricket::Session* session = sessions_.front()->ReleaseSession();
+ cricket_session_manager_->DestroySession(session);
+ sessions_.pop_front();
+ }
+ closed_ = true;
+ }
+
+ closed_task->Run();
+ delete closed_task;
+}
+
+void JingleSessionManager::set_allow_local_ips(bool allow_local_ips) {
+ allow_local_ips_ = allow_local_ips;
+}
+
+scoped_refptr<protocol::Session> JingleSessionManager::Connect(
+ const std::string& jid,
+ CandidateChromotocolConfig* chromotocol_config,
+ protocol::Session::StateChangeCallback* state_change_callback) {
+ // Can be called from any thread.
+ scoped_refptr<JingleSession> jingle_session(
+ new JingleSession(this));
+ jingle_session->set_candidate_config(chromotocol_config);
+ message_loop()->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &JingleSessionManager::DoConnect,
+ jingle_session, jid,
+ state_change_callback));
+ return jingle_session;
+}
+
+void JingleSessionManager::DoConnect(
+ scoped_refptr<JingleSession> jingle_session,
+ const std::string& jid,
+ protocol::Session::StateChangeCallback* state_change_callback) {
+ DCHECK_EQ(message_loop(), MessageLoop::current());
+ cricket::Session* cricket_session = cricket_session_manager_->CreateSession(
+ local_jid_, kChromotingXmlNamespace);
+
+ // Initialize connection object before we send initiate stanza.
+ jingle_session->SetStateChangeCallback(state_change_callback);
+ jingle_session->Init(cricket_session);
+ sessions_.push_back(jingle_session);
+
+ cricket_session->Initiate(
+ jid,
+ CreateSessionDescription(jingle_session->candidate_config()->Clone()));
+}
+
+JingleThread* JingleSessionManager::jingle_thread() {
+ return jingle_thread_;
+}
+
+MessageLoop* JingleSessionManager::message_loop() {
+ return jingle_thread_->message_loop();
+}
+
+void JingleSessionManager::OnSessionCreate(
+ cricket::Session* cricket_session, bool incoming) {
+ DCHECK_EQ(message_loop(), MessageLoop::current());
+
+ // Allow local connections if neccessary.
+ cricket_session->set_allow_local_ips(allow_local_ips_);
+
+ // If this is an outcoming session the connection object is already
+ // created.
+ if (incoming) {
+ JingleSession* jingle_session = new JingleSession(this);
+ sessions_.push_back(make_scoped_refptr(jingle_session));
+ jingle_session->Init(cricket_session);
+ }
+}
+
+void JingleSessionManager::OnSessionDestroy(cricket::Session* cricket_session) {
+ DCHECK_EQ(message_loop(), MessageLoop::current());
+
+ std::list<scoped_refptr<JingleSession> >::iterator it;
+ for (it = sessions_.begin(); it != sessions_.end(); ++it) {
+ if ((*it)->HasSession(cricket_session)) {
+ (*it)->ReleaseSession();
+ sessions_.erase(it);
+ return;
+ }
+ }
+}
+
+void JingleSessionManager::AcceptConnection(
+ JingleSession* jingle_session,
+ cricket::Session* cricket_session) {
+ DCHECK_EQ(message_loop(), MessageLoop::current());
+
+ // Reject connection if we are closed.
+ if (closed_) {
+ cricket_session->Reject(cricket::STR_TERMINATE_DECLINE);
+ return;
+ }
+
+ const cricket::SessionDescription* session_description =
+ cricket_session->remote_description();
+ const cricket::ContentInfo* content =
+ session_description->FirstContentByType(kChromotingXmlNamespace);
+
+ CHECK(content);
+
+ const ContentDescription* content_description =
+ static_cast<const ContentDescription*>(content->description);
+ jingle_session->set_candidate_config(content_description->config()->Clone());
+
+ IncomingSessionResponse response = protocol::SessionManager::DECLINE;
+
+ // Always reject connection if there is no callback.
+ if (incoming_session_callback_.get())
+ incoming_session_callback_->Run(jingle_session, &response);
+
+ switch (response) {
+ case protocol::SessionManager::ACCEPT: {
+ // Connection must be configured by the callback.
+ DCHECK(jingle_session->config());
+ CandidateChromotocolConfig* candidate_config =
+ CandidateChromotocolConfig::CreateFrom(jingle_session->config());
+ cricket_session->Accept(CreateSessionDescription(candidate_config));
+ break;
+ }
+
+ case protocol::SessionManager::INCOMPATIBLE: {
+ cricket_session->Reject(cricket::STR_TERMINATE_INCOMPATIBLE_PARAMETERS);
+ break;
+ }
+
+ case protocol::SessionManager::DECLINE: {
+ cricket_session->Reject(cricket::STR_TERMINATE_DECLINE);
+ break;
+ }
+
+ default: {
+ NOTREACHED();
+ }
+ }
+}
+
+// Parse content description generated by WriteContent().
+bool JingleSessionManager::ParseContent(
+ cricket::SignalingProtocol protocol,
+ const XmlElement* element,
+ const cricket::ContentDescription** content,
+ cricket::ParseError* error) {
+ if (element->Name() == QName(kChromotingXmlNamespace, kDescriptionTag)) {
+ scoped_ptr<CandidateChromotocolConfig> config(
+ CandidateChromotocolConfig::CreateEmpty());
+ const XmlElement* child = NULL;
+
+ // <control> tags.
+ QName control_tag(kChromotingXmlNamespace, kControlTag);
+ child = element->FirstNamed(control_tag);
+ while (child) {
+ ChannelConfig channel_config;
+ if (!ParseChannelConfig(child, false, &channel_config))
+ return false;
+ config->AddControlConfig(channel_config);
+ child = element->NextNamed(control_tag);
+ }
+
+ // <event> tags.
+ QName event_tag(kChromotingXmlNamespace, kEventTag);
+ child = element->FirstNamed(event_tag);
+ while (child) {
+ ChannelConfig channel_config;
+ if (!ParseChannelConfig(child, false, &channel_config))
+ return false;
+ config->AddEventConfig(channel_config);
+ child = element->NextNamed(event_tag);
+ }
+
+ // <video> tags.
+ QName video_tag(kChromotingXmlNamespace, kVideoTag);
+ child = element->FirstNamed(video_tag);
+ while (child) {
+ ChannelConfig channel_config;
+ if (!ParseChannelConfig(child, true, &channel_config))
+ return false;
+ config->AddVideoConfig(channel_config);
+ child = element->NextNamed(video_tag);
+ }
+
+ // <initial-resolution> tag.
+ child = element->FirstNamed(QName(kChromotingXmlNamespace, kResolutionTag));
+ if (!child)
+ return false; // Resolution must always be specified.
+ int width;
+ int height;
+ if (!base::StringToInt(child->Attr(QName(kDefaultNs, kWidthAttr)),
+ &width) ||
+ !base::StringToInt(child->Attr(QName(kDefaultNs, kHeightAttr)),
+ &height)) {
+ return false;
+ }
+ ScreenResolution resolution(width, height);
+ if (!resolution.IsValid()) {
+ return false;
+ }
+
+ config->SetInitialResolution(resolution);
+
+ *content = new ContentDescription(config.release());
+ return true;
+ }
+ LOG(ERROR) << "Invalid description: " << element->Str();
+ return false;
+}
+
+// WriteContent creates content description for chromoting session. The
+// description looks as follows:
+// <description xmlns="google:remoting">
+// <control transport="stream" version="1" />
+// <event transport="datagram" version="1" />
+// <video transport="srtp" codec="vp8" version="1" />
+// <initial-resolution width="800" height="600" />
+// </description>
+//
+bool JingleSessionManager::WriteContent(
+ cricket::SignalingProtocol protocol,
+ const cricket::ContentDescription* content,
+ XmlElement** elem,
+ cricket::WriteError* error) {
+ const ContentDescription* desc =
+ static_cast<const ContentDescription*>(content);
+
+ XmlElement* root = new XmlElement(
+ QName(kChromotingXmlNamespace, kDescriptionTag), true);
+
+ const CandidateChromotocolConfig* config = desc->config();
+ std::vector<ChannelConfig>::const_iterator it;
+
+ for (it = config->control_configs().begin();
+ it != config->control_configs().end(); ++it) {
+ root->AddElement(FormatChannelConfig(*it, kControlTag));
+ }
+
+ for (it = config->event_configs().begin();
+ it != config->event_configs().end(); ++it) {
+ root->AddElement(FormatChannelConfig(*it, kEventTag));
+ }
+
+ for (it = config->video_configs().begin();
+ it != config->video_configs().end(); ++it) {
+ root->AddElement(FormatChannelConfig(*it, kVideoTag));
+ }
+
+ XmlElement* resolution_tag = new XmlElement(
+ QName(kChromotingXmlNamespace, kResolutionTag));
+ resolution_tag->AddAttr(QName(kDefaultNs, kWidthAttr),
+ base::IntToString(
+ config->initial_resolution().width));
+ resolution_tag->AddAttr(QName(kDefaultNs, kHeightAttr),
+ base::IntToString(
+ config->initial_resolution().height));
+ root->AddElement(resolution_tag);
+
+ *elem = root;
+ return true;
+}
+
+SessionDescription* JingleSessionManager::CreateSessionDescription(
+ const CandidateChromotocolConfig* config) {
+ SessionDescription* desc = new SessionDescription();
+ desc->AddContent(JingleSession::kChromotingContentName,
+ kChromotingXmlNamespace,
+ new ContentDescription(config));
+ return desc;
+}
+
+} // namespace protocol
+
+} // namespace remoting