| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "remoting/protocol/jingle_session.h" | 5 #include "remoting/protocol/jingle_session.h" |
| 6 | 6 |
| 7 #include "base/base64.h" |
| 7 #include "base/bind.h" | 8 #include "base/bind.h" |
| 8 #include "base/location.h" | 9 #include "base/location.h" |
| 9 #include "base/message_loop_proxy.h" | 10 #include "base/message_loop_proxy.h" |
| 10 #include "base/rand_util.h" | 11 #include "base/rand_util.h" |
| 11 #include "base/stl_util.h" | 12 #include "base/stl_util.h" |
| 12 #include "crypto/hmac.h" | 13 #include "crypto/hmac.h" |
| 13 #include "crypto/rsa_private_key.h" | |
| 14 #include "net/base/net_errors.h" | 14 #include "net/base/net_errors.h" |
| 15 #include "net/socket/stream_socket.h" | 15 #include "net/socket/stream_socket.h" |
| 16 #include "remoting/base/constants.h" | 16 #include "remoting/base/constants.h" |
| 17 #include "remoting/protocol/auth_util.h" | 17 #include "remoting/protocol/auth_util.h" |
| 18 #include "remoting/protocol/authenticator.h" |
| 19 #include "remoting/protocol/channel_authenticator.h" |
| 18 #include "remoting/protocol/jingle_datagram_connector.h" | 20 #include "remoting/protocol/jingle_datagram_connector.h" |
| 19 #include "remoting/protocol/jingle_session_manager.h" | 21 #include "remoting/protocol/jingle_session_manager.h" |
| 20 #include "remoting/protocol/jingle_stream_connector.h" | 22 #include "remoting/protocol/jingle_stream_connector.h" |
| 21 #include "remoting/protocol/v1_client_channel_authenticator.h" | |
| 22 #include "remoting/protocol/v1_host_channel_authenticator.h" | |
| 23 #include "third_party/libjingle/source/talk/base/thread.h" | 23 #include "third_party/libjingle/source/talk/base/thread.h" |
| 24 #include "third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.h" | 24 #include "third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.h" |
| 25 #include "third_party/libjingle/source/talk/p2p/base/session.h" | 25 #include "third_party/libjingle/source/talk/p2p/base/session.h" |
| 26 #include "third_party/libjingle/source/talk/p2p/base/transport.h" | 26 #include "third_party/libjingle/source/talk/p2p/base/transport.h" |
| 27 | 27 |
| 28 using cricket::BaseSession; | 28 using cricket::BaseSession; |
| 29 | 29 |
| 30 namespace remoting { | 30 namespace remoting { |
| 31 namespace protocol { | 31 namespace protocol { |
| 32 | 32 |
| 33 // static | |
| 34 JingleSession* JingleSession::CreateClientSession( | |
| 35 JingleSessionManager* manager, const std::string& host_public_key) { | |
| 36 return new JingleSession(manager, "", NULL, host_public_key); | |
| 37 } | |
| 38 | |
| 39 // static | |
| 40 JingleSession* JingleSession::CreateServerSession( | |
| 41 JingleSessionManager* manager, | |
| 42 const std::string& certificate, | |
| 43 crypto::RSAPrivateKey* key) { | |
| 44 return new JingleSession(manager, certificate, key, ""); | |
| 45 } | |
| 46 | |
| 47 JingleSession::JingleSession( | 33 JingleSession::JingleSession( |
| 48 JingleSessionManager* jingle_session_manager, | 34 JingleSessionManager* jingle_session_manager, |
| 49 const std::string& local_cert, | 35 cricket::Session* cricket_session, |
| 50 crypto::RSAPrivateKey* local_private_key, | 36 Authenticator* authenticator) |
| 51 const std::string& peer_public_key) | |
| 52 : jingle_session_manager_(jingle_session_manager), | 37 : jingle_session_manager_(jingle_session_manager), |
| 53 local_cert_(local_cert), | 38 authenticator_(authenticator), |
| 54 state_(INITIALIZING), | 39 state_(INITIALIZING), |
| 55 error_(OK), | 40 error_(OK), |
| 56 closing_(false), | 41 closing_(false), |
| 57 cricket_session_(NULL), | 42 cricket_session_(cricket_session), |
| 58 config_set_(false), | 43 config_set_(false), |
| 59 ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)) { | 44 ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)) { |
| 60 // TODO(hclam): Need a better way to clone a key. | 45 jid_ = cricket_session_->remote_name(); |
| 61 if (local_private_key) { | 46 cricket_session_->SignalState.connect(this, &JingleSession::OnSessionState); |
| 62 std::vector<uint8> key_bytes; | 47 cricket_session_->SignalError.connect(this, &JingleSession::OnSessionError); |
| 63 CHECK(local_private_key->ExportPrivateKey(&key_bytes)); | |
| 64 local_private_key_.reset( | |
| 65 crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(key_bytes)); | |
| 66 CHECK(local_private_key_.get()); | |
| 67 } | |
| 68 } | 48 } |
| 69 | 49 |
| 70 JingleSession::~JingleSession() { | 50 JingleSession::~JingleSession() { |
| 71 // Reset the callback so that it's not called from Close(). | 51 // Reset the callback so that it's not called from Close(). |
| 72 state_change_callback_.Reset(); | 52 state_change_callback_.Reset(); |
| 73 Close(); | 53 Close(); |
| 74 jingle_session_manager_->SessionDestroyed(this); | 54 jingle_session_manager_->SessionDestroyed(this); |
| 75 DCHECK(channel_connectors_.empty()); | 55 DCHECK(channel_connectors_.empty()); |
| 76 } | 56 } |
| 77 | 57 |
| 78 void JingleSession::Init(cricket::Session* cricket_session) { | 58 void JingleSession::SendSessionInitiate() { |
| 79 DCHECK(CalledOnValidThread()); | 59 DCHECK_EQ(authenticator_->state(), Authenticator::MESSAGE_READY); |
| 80 | 60 cricket_session_->Initiate( |
| 81 cricket_session_ = cricket_session; | 61 jid_, CreateSessionDescription(candidate_config()->Clone(), |
| 82 jid_ = cricket_session_->remote_name(); | 62 authenticator_->GetNextMessage())); |
| 83 cricket_session_->SignalState.connect( | |
| 84 this, &JingleSession::OnSessionState); | |
| 85 cricket_session_->SignalError.connect( | |
| 86 this, &JingleSession::OnSessionError); | |
| 87 } | 63 } |
| 88 | 64 |
| 89 void JingleSession::CloseInternal(int result, Error error) { | 65 void JingleSession::CloseInternal(int result, Error error) { |
| 90 DCHECK(CalledOnValidThread()); | 66 DCHECK(CalledOnValidThread()); |
| 91 | 67 |
| 92 if (state_ != FAILED && state_ != CLOSED && !closing_) { | 68 if (state_ != FAILED && state_ != CLOSED && !closing_) { |
| 93 closing_ = true; | 69 closing_ = true; |
| 94 | 70 |
| 95 // Tear down the cricket session, including the cricket transport channels. | 71 // Tear down the cricket session, including the cricket transport channels. |
| 96 if (cricket_session_) { | 72 if (cricket_session_) { |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 192 } | 168 } |
| 193 | 169 |
| 194 void JingleSession::set_candidate_config( | 170 void JingleSession::set_candidate_config( |
| 195 const CandidateSessionConfig* candidate_config) { | 171 const CandidateSessionConfig* candidate_config) { |
| 196 DCHECK(CalledOnValidThread()); | 172 DCHECK(CalledOnValidThread()); |
| 197 DCHECK(!candidate_config_.get()); | 173 DCHECK(!candidate_config_.get()); |
| 198 DCHECK(candidate_config); | 174 DCHECK(candidate_config); |
| 199 candidate_config_.reset(candidate_config); | 175 candidate_config_.reset(candidate_config); |
| 200 } | 176 } |
| 201 | 177 |
| 202 const std::string& JingleSession::local_certificate() const { | |
| 203 DCHECK(CalledOnValidThread()); | |
| 204 return local_cert_; | |
| 205 } | |
| 206 | |
| 207 const SessionConfig& JingleSession::config() { | 178 const SessionConfig& JingleSession::config() { |
| 208 DCHECK(CalledOnValidThread()); | 179 DCHECK(CalledOnValidThread()); |
| 209 DCHECK(config_set_); | 180 DCHECK(config_set_); |
| 210 return config_; | 181 return config_; |
| 211 } | 182 } |
| 212 | 183 |
| 213 void JingleSession::set_config(const SessionConfig& config) { | 184 void JingleSession::set_config(const SessionConfig& config) { |
| 214 DCHECK(CalledOnValidThread()); | 185 DCHECK(CalledOnValidThread()); |
| 215 DCHECK(!config_set_); | 186 DCHECK(!config_set_); |
| 216 config_ = config; | 187 config_ = config; |
| 217 config_set_ = true; | 188 config_set_ = true; |
| 218 } | 189 } |
| 219 | 190 |
| 220 const std::string& JingleSession::initiator_token() { | |
| 221 DCHECK(CalledOnValidThread()); | |
| 222 return initiator_token_; | |
| 223 } | |
| 224 | |
| 225 void JingleSession::set_initiator_token(const std::string& initiator_token) { | |
| 226 DCHECK(CalledOnValidThread()); | |
| 227 initiator_token_ = initiator_token; | |
| 228 } | |
| 229 | |
| 230 const std::string& JingleSession::receiver_token() { | |
| 231 DCHECK(CalledOnValidThread()); | |
| 232 return receiver_token_; | |
| 233 } | |
| 234 | |
| 235 void JingleSession::set_receiver_token(const std::string& receiver_token) { | |
| 236 DCHECK(CalledOnValidThread()); | |
| 237 receiver_token_ = receiver_token; | |
| 238 } | |
| 239 | |
| 240 void JingleSession::set_shared_secret(const std::string& secret) { | |
| 241 DCHECK(CalledOnValidThread()); | |
| 242 shared_secret_ = secret; | |
| 243 } | |
| 244 | |
| 245 const std::string& JingleSession::shared_secret() { | |
| 246 DCHECK(CalledOnValidThread()); | |
| 247 return shared_secret_; | |
| 248 } | |
| 249 | |
| 250 | |
| 251 void JingleSession::Close() { | 191 void JingleSession::Close() { |
| 252 DCHECK(CalledOnValidThread()); | 192 DCHECK(CalledOnValidThread()); |
| 253 | 193 |
| 254 CloseInternal(net::ERR_CONNECTION_CLOSED, OK); | 194 CloseInternal(net::ERR_CONNECTION_CLOSED, OK); |
| 255 } | 195 } |
| 256 | 196 |
| 257 void JingleSession::OnSessionState( | 197 void JingleSession::OnSessionState( |
| 258 BaseSession* session, BaseSession::State state) { | 198 BaseSession* session, BaseSession::State state) { |
| 259 DCHECK(CalledOnValidThread()); | 199 DCHECK(CalledOnValidThread()); |
| 260 DCHECK_EQ(cricket_session_, session); | 200 DCHECK_EQ(cricket_session_, session); |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 301 if (error != cricket::Session::ERROR_NONE) { | 241 if (error != cricket::Session::ERROR_NONE) { |
| 302 // TODO(sergeyu): Report different errors depending on |error|. | 242 // TODO(sergeyu): Report different errors depending on |error|. |
| 303 CloseInternal(net::ERR_CONNECTION_ABORTED, CHANNEL_CONNECTION_ERROR); | 243 CloseInternal(net::ERR_CONNECTION_ABORTED, CHANNEL_CONNECTION_ERROR); |
| 304 } | 244 } |
| 305 } | 245 } |
| 306 | 246 |
| 307 void JingleSession::OnInitiate() { | 247 void JingleSession::OnInitiate() { |
| 308 DCHECK(CalledOnValidThread()); | 248 DCHECK(CalledOnValidThread()); |
| 309 jid_ = cricket_session_->remote_name(); | 249 jid_ = cricket_session_->remote_name(); |
| 310 | 250 |
| 311 if (!cricket_session_->initiator()) { | |
| 312 const protocol::ContentDescription* content_description = | |
| 313 static_cast<const protocol::ContentDescription*>( | |
| 314 GetContentInfo()->description); | |
| 315 CHECK(content_description); | |
| 316 } | |
| 317 | |
| 318 if (cricket_session_->initiator()) { | 251 if (cricket_session_->initiator()) { |
| 319 // Set state to CONNECTING if this is an outgoing message. We need | 252 // Set state to CONNECTING if this is an outgoing message. We need |
| 320 // to post this task because channel creation works only after we | 253 // to post this task because channel creation works only after we |
| 321 // return from this method. This is because | 254 // return from this method. This is because |
| 322 // JingleChannelConnector::Connect() needs to call | 255 // JingleChannelConnector::Connect() needs to call |
| 323 // set_incoming_only() on P2PTransportChannel, but | 256 // set_incoming_only() on P2PTransportChannel, but |
| 324 // P2PTransportChannel is created only after we return from this | 257 // P2PTransportChannel is created only after we return from this |
| 325 // method. | 258 // method. |
| 326 // TODO(sergeyu): Add set_incoming_only() in TransportChannelProxy. | 259 // TODO(sergeyu): Add set_incoming_only() in TransportChannelProxy. |
| 327 jingle_session_manager_->message_loop_->PostTask( | 260 jingle_session_manager_->message_loop_->PostTask( |
| (...skipping 10 matching lines...) Expand all Loading... |
| 338 const cricket::SessionDescription* description) { | 271 const cricket::SessionDescription* description) { |
| 339 // We should only be called after ParseContent has succeeded, in which | 272 // We should only be called after ParseContent has succeeded, in which |
| 340 // case there will always be a Chromoting session configuration. | 273 // case there will always be a Chromoting session configuration. |
| 341 const cricket::ContentInfo* content = | 274 const cricket::ContentInfo* content = |
| 342 description->FirstContentByType(kChromotingXmlNamespace); | 275 description->FirstContentByType(kChromotingXmlNamespace); |
| 343 CHECK(content); | 276 CHECK(content); |
| 344 const protocol::ContentDescription* content_description = | 277 const protocol::ContentDescription* content_description = |
| 345 static_cast<const protocol::ContentDescription*>(content->description); | 278 static_cast<const protocol::ContentDescription*>(content->description); |
| 346 CHECK(content_description); | 279 CHECK(content_description); |
| 347 | 280 |
| 348 remote_cert_ = content_description->certificate(); | 281 // Process authenticator message. |
| 349 if (remote_cert_.empty()) { | 282 const buzz::XmlElement* auth_message = |
| 350 LOG(ERROR) << "Connection response does not specify certificate"; | 283 content_description->authenticator_message(); |
| 284 if (!auth_message) { |
| 285 DLOG(WARNING) << "Received session-accept without authentication message " |
| 286 << auth_message->Str(); |
| 351 return false; | 287 return false; |
| 352 } | 288 } |
| 353 | 289 |
| 290 DCHECK(authenticator_->state() == Authenticator::WAITING_MESSAGE); |
| 291 authenticator_->ProcessMessage(auth_message); |
| 292 // Support for more than two auth message is not implemented yet. |
| 293 DCHECK(authenticator_->state() != Authenticator::WAITING_MESSAGE && |
| 294 authenticator_->state() != Authenticator::MESSAGE_READY); |
| 295 |
| 296 if (authenticator_->state() != Authenticator::ACCEPTED) { |
| 297 return false; |
| 298 } |
| 299 |
| 354 SessionConfig config; | 300 SessionConfig config; |
| 355 if (!content_description->config()->GetFinalConfig(&config)) { | 301 if (!content_description->config()->GetFinalConfig(&config)) { |
| 356 LOG(ERROR) << "Connection response does not specify configuration"; | 302 LOG(ERROR) << "Connection response does not specify configuration"; |
| 357 return false; | 303 return false; |
| 358 } | 304 } |
| 359 if (!candidate_config()->IsSupported(config)) { | 305 if (!candidate_config()->IsSupported(config)) { |
| 360 LOG(ERROR) << "Connection response specifies an invalid configuration"; | 306 LOG(ERROR) << "Connection response specifies an invalid configuration"; |
| 361 return false; | 307 return false; |
| 362 } | 308 } |
| 363 | 309 |
| (...skipping 18 matching lines...) Expand all Loading... |
| 382 } | 328 } |
| 383 | 329 |
| 384 void JingleSession::OnTerminate() { | 330 void JingleSession::OnTerminate() { |
| 385 DCHECK(CalledOnValidThread()); | 331 DCHECK(CalledOnValidThread()); |
| 386 CloseInternal(net::ERR_CONNECTION_ABORTED, OK); | 332 CloseInternal(net::ERR_CONNECTION_ABORTED, OK); |
| 387 } | 333 } |
| 388 | 334 |
| 389 void JingleSession::AcceptConnection() { | 335 void JingleSession::AcceptConnection() { |
| 390 SetState(CONNECTING); | 336 SetState(CONNECTING); |
| 391 | 337 |
| 392 if (!jingle_session_manager_->AcceptConnection(this, cricket_session_)) { | 338 const cricket::SessionDescription* session_description = |
| 339 cricket_session_->remote_description(); |
| 340 const cricket::ContentInfo* content = |
| 341 session_description->FirstContentByType(kChromotingXmlNamespace); |
| 342 |
| 343 CHECK(content); |
| 344 const ContentDescription* content_description = |
| 345 static_cast<const ContentDescription*>(content->description); |
| 346 candidate_config_.reset(content_description->config()->Clone()); |
| 347 |
| 348 SessionManager::IncomingSessionResponse response = |
| 349 jingle_session_manager_->AcceptConnection(this); |
| 350 if (response != SessionManager::ACCEPT) { |
| 351 if (response == SessionManager::INCOMPATIBLE) { |
| 352 cricket_session_->TerminateWithReason( |
| 353 cricket::STR_TERMINATE_INCOMPATIBLE_PARAMETERS); |
| 354 } else { |
| 355 cricket_session_->TerminateWithReason(cricket::STR_TERMINATE_DECLINE); |
| 356 } |
| 393 Close(); | 357 Close(); |
| 394 // Release session so that JingleSessionManager::SessionDestroyed() | 358 // Release session so that JingleSessionManager::SessionDestroyed() |
| 395 // doesn't try to call cricket::SessionManager::DestroySession() for it. | 359 // doesn't try to call cricket::SessionManager::DestroySession() for it. |
| 396 ReleaseSession(); | 360 ReleaseSession(); |
| 397 delete this; | 361 delete this; |
| 398 return; | 362 return; |
| 399 } | 363 } |
| 400 | 364 |
| 401 if (!VerifySupportAuthToken(jid_, shared_secret_, initiator_token())) | 365 const buzz::XmlElement* auth_message = |
| 366 content_description->authenticator_message(); |
| 367 if (!auth_message) { |
| 368 DLOG(WARNING) << "Received session-initiate without authenticator message."; |
| 369 CloseInternal(net::ERR_CONNECTION_FAILED, INCOMPATIBLE_PROTOCOL); |
| 370 return; |
| 371 } |
| 372 |
| 373 authenticator_.reset( |
| 374 jingle_session_manager_->CreateAuthenticator(jid(), auth_message)); |
| 375 if (!authenticator_.get()) { |
| 376 CloseInternal(net::ERR_CONNECTION_FAILED, INCOMPATIBLE_PROTOCOL); |
| 377 return; |
| 378 } |
| 379 |
| 380 DCHECK(authenticator_->state() == Authenticator::WAITING_MESSAGE); |
| 381 authenticator_->ProcessMessage(auth_message); |
| 382 // Support for more than two auth message is not implemented yet. |
| 383 DCHECK(authenticator_->state() != Authenticator::WAITING_MESSAGE); |
| 384 if (authenticator_->state() == Authenticator::REJECTED) { |
| 402 CloseInternal(net::ERR_CONNECTION_FAILED, AUTHENTICATION_FAILED); | 385 CloseInternal(net::ERR_CONNECTION_FAILED, AUTHENTICATION_FAILED); |
| 386 return; |
| 387 } |
| 388 |
| 389 // Connection must be configured by the AcceptConnection() callback. |
| 390 CandidateSessionConfig* candidate_config = |
| 391 CandidateSessionConfig::CreateFrom(config()); |
| 392 |
| 393 buzz::XmlElement* auth_reply = NULL; |
| 394 if (authenticator_->state() == Authenticator::MESSAGE_READY) |
| 395 auth_reply = authenticator_->GetNextMessage(); |
| 396 DCHECK_EQ(authenticator_->state(), Authenticator::ACCEPTED); |
| 397 cricket_session_->Accept( |
| 398 CreateSessionDescription(candidate_config, auth_reply)); |
| 403 } | 399 } |
| 404 | 400 |
| 405 void JingleSession::AddChannelConnector( | 401 void JingleSession::AddChannelConnector( |
| 406 const std::string& name, JingleChannelConnector* connector) { | 402 const std::string& name, JingleChannelConnector* connector) { |
| 407 DCHECK(channel_connectors_.find(name) == channel_connectors_.end()); | 403 DCHECK(channel_connectors_.find(name) == channel_connectors_.end()); |
| 408 | 404 |
| 409 const std::string& content_name = GetContentInfo()->name; | 405 const std::string& content_name = GetContentInfo()->name; |
| 410 cricket::TransportChannel* raw_channel = | 406 cricket::TransportChannel* raw_channel = |
| 411 cricket_session_->CreateChannel(content_name, name); | 407 cricket_session_->CreateChannel(content_name, name); |
| 412 | 408 |
| 413 if (!jingle_session_manager_->allow_nat_traversal_ && | 409 if (!jingle_session_manager_->allow_nat_traversal_ && |
| 414 !cricket_session_->initiator()) { | 410 !cricket_session_->initiator()) { |
| 415 // Don't make outgoing connections from the host to client when | 411 // Don't make outgoing connections from the host to client when |
| 416 // NAT traversal is disabled. | 412 // NAT traversal is disabled. |
| 417 raw_channel->GetP2PChannel()->set_incoming_only(true); | 413 raw_channel->GetP2PChannel()->set_incoming_only(true); |
| 418 } | 414 } |
| 419 | 415 |
| 420 channel_connectors_[name] = connector; | 416 channel_connectors_[name] = connector; |
| 421 ChannelAuthenticator* authenticator; | 417 ChannelAuthenticator* authenticator = |
| 422 if (cricket_session_->initiator()) { | 418 authenticator_->CreateChannelAuthenticator(); |
| 423 authenticator = new V1ClientChannelAuthenticator( | |
| 424 remote_cert_, shared_secret_); | |
| 425 } else { | |
| 426 authenticator = new V1HostChannelAuthenticator( | |
| 427 local_cert_, local_private_key_.get(), shared_secret_); | |
| 428 } | |
| 429 connector->Connect(authenticator, raw_channel); | 419 connector->Connect(authenticator, raw_channel); |
| 430 | 420 |
| 431 // Workaround bug in libjingle - it doesn't connect channels if they | 421 // Workaround bug in libjingle - it doesn't connect channels if they |
| 432 // are created after the session is accepted. See crbug.com/89384. | 422 // are created after the session is accepted. See crbug.com/89384. |
| 433 // TODO(sergeyu): Fix the bug and remove this line. | 423 // TODO(sergeyu): Fix the bug and remove this line. |
| 434 cricket_session_->GetTransport(content_name)->ConnectChannels(); | 424 cricket_session_->GetTransport(content_name)->ConnectChannels(); |
| 435 } | 425 } |
| 436 | 426 |
| 437 void JingleSession::OnChannelConnectorFinished( | 427 void JingleSession::OnChannelConnectorFinished( |
| 438 const std::string& name, JingleChannelConnector* connector) { | 428 const std::string& name, JingleChannelConnector* connector) { |
| (...skipping 23 matching lines...) Expand all Loading... |
| 462 if (new_state != state_) { | 452 if (new_state != state_) { |
| 463 DCHECK_NE(state_, CLOSED); | 453 DCHECK_NE(state_, CLOSED); |
| 464 DCHECK_NE(state_, FAILED); | 454 DCHECK_NE(state_, FAILED); |
| 465 | 455 |
| 466 state_ = new_state; | 456 state_ = new_state; |
| 467 if (!state_change_callback_.is_null()) | 457 if (!state_change_callback_.is_null()) |
| 468 state_change_callback_.Run(new_state); | 458 state_change_callback_.Run(new_state); |
| 469 } | 459 } |
| 470 } | 460 } |
| 471 | 461 |
| 462 // static |
| 463 cricket::SessionDescription* JingleSession::CreateSessionDescription( |
| 464 const CandidateSessionConfig* config, |
| 465 const buzz::XmlElement* authenticator_message) { |
| 466 cricket::SessionDescription* desc = new cricket::SessionDescription(); |
| 467 desc->AddContent( |
| 468 ContentDescription::kChromotingContentName, kChromotingXmlNamespace, |
| 469 new ContentDescription(config, authenticator_message)); |
| 470 return desc; |
| 471 } |
| 472 |
| 472 } // namespace protocol | 473 } // namespace protocol |
| 473 } // namespace remoting | 474 } // namespace remoting |
| OLD | NEW |