summaryrefslogtreecommitdiffstats
path: root/remoting
diff options
context:
space:
mode:
Diffstat (limited to 'remoting')
-rw-r--r--remoting/host/chromoting_host.cc211
-rw-r--r--remoting/host/chromoting_host.h67
-rw-r--r--remoting/host/chromoting_host_context.cc56
-rw-r--r--remoting/host/chromoting_host_context.h51
-rw-r--r--remoting/host/chromoting_host_context_unittest.cc28
-rw-r--r--remoting/host/client_connection.cc27
-rw-r--r--remoting/host/client_connection.h5
-rw-r--r--remoting/host/client_connection_unittest.cc31
-rw-r--r--remoting/host/encoder_verbatim.cc3
-rw-r--r--remoting/host/session_manager.cc53
-rw-r--r--remoting/host/session_manager.h10
-rw-r--r--remoting/host/simple_host_process.cc27
-rw-r--r--remoting/remoting.gyp3
13 files changed, 408 insertions, 164 deletions
diff --git a/remoting/host/chromoting_host.cc b/remoting/host/chromoting_host.cc
index 0fc60e2..8510678 100644
--- a/remoting/host/chromoting_host.cc
+++ b/remoting/host/chromoting_host.cc
@@ -5,117 +5,63 @@
#include "remoting/host/chromoting_host.h"
#include "base/stl_util-inl.h"
-#include "base/waitable_event.h"
+#include "base/task.h"
#include "build/build_config.h"
#include "remoting/base/constants.h"
#include "remoting/base/protocol_decoder.h"
+#include "remoting/host/chromoting_host_context.h"
#include "remoting/host/host_config.h"
#include "remoting/host/session_manager.h"
#include "remoting/jingle_glue/jingle_channel.h"
namespace remoting {
-ChromotingHost::ChromotingHost(MutableHostConfig* config,
+ChromotingHost::ChromotingHost(ChromotingHostContext* context,
+ MutableHostConfig* config,
Capturer* capturer,
Encoder* encoder,
- EventExecutor* executor,
- base::WaitableEvent* host_done)
- : main_thread_("MainThread"),
- capture_thread_("CaptureThread"),
- encode_thread_("EncodeThread"),
- config_(config),
- capturer_(capturer),
- encoder_(encoder),
- executor_(executor),
- host_done_(host_done) {
- // TODO(ajwong): The thread injection and object ownership is odd here.
- // Fix so we do not start this thread in the constructor, so we only
- // take in a session manager, don't let session manager own the
- // capturer/encoder, and then associate the capturer and encoder threads with
- // the capturer and encoder objects directly. This will require a
- // non-refcounted NewRunnableMethod.
- main_thread_.StartWithOptions(
- base::Thread::Options(MessageLoop::TYPE_UI, 0));
- network_thread_.Start();
+ EventExecutor* executor)
+ : context_(context),
+ config_(config),
+ capturer_(capturer),
+ encoder_(encoder),
+ executor_(executor),
+ state_(kInitial) {
}
ChromotingHost::~ChromotingHost() {
- // TODO(ajwong): We really need to inject these threads and get rid of these
- // start/stops.
- main_thread_.Stop();
- network_thread_.Stop();
- DCHECK(!encode_thread_.IsRunning());
- DCHECK(!capture_thread_.IsRunning());
}
-void ChromotingHost::Run() {
+void ChromotingHost::Start(Task* shutdown_task) {
// Submit a task to perform host registration. We'll also start
// listening to connection if registration is done.
- message_loop()->PostTask(
+ context_->main_message_loop()->PostTask(
FROM_HERE,
- NewRunnableMethod(this, &ChromotingHost::RegisterHost));
+ NewRunnableMethod(this, &ChromotingHost::DoStart, shutdown_task));
}
// This method is called when we need to destroy the host process.
-void ChromotingHost::DestroySession() {
- DCHECK_EQ(message_loop(), MessageLoop::current());
-
- // First we tell the session to pause and then we wait until all
- // the tasks are done.
- if (session_.get()) {
- session_->Pause();
-
- // TODO(hclam): Revise the order.
- DCHECK(encode_thread_.IsRunning());
- encode_thread_.Stop();
-
- DCHECK(capture_thread_.IsRunning());
- capture_thread_.Stop();
- }
-}
-
-// This method talks to the cloud to register the host process. If
-// successful we will start listening to network requests.
-void ChromotingHost::RegisterHost() {
- DCHECK_EQ(message_loop(), MessageLoop::current());
- DCHECK(!jingle_client_);
-
- std::string xmpp_login;
- std::string xmpp_auth_token;
- if (!config_->GetString(kXmppLoginConfigPath, &xmpp_login) ||
- !config_->GetString(kXmppAuthTokenConfigPath, &xmpp_auth_token)) {
- LOG(ERROR) << "XMMP credentials are not defined in config.";
- return;
- }
-
- // Connect to the talk network with a JingleClient.
- jingle_client_ = new JingleClient(&network_thread_);
- jingle_client_->Init(xmpp_login, xmpp_auth_token,
- kChromotingTokenServiceName, this);
+void ChromotingHost::Shutdown() {
+ context_->main_message_loop()->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &ChromotingHost::DoShutdown));
}
// This method is called if a client is connected to this object.
void ChromotingHost::OnClientConnected(ClientConnection* client) {
- DCHECK_EQ(message_loop(), MessageLoop::current());
+ DCHECK_EQ(context_->main_message_loop(), MessageLoop::current());
// Create a new RecordSession if there was none.
if (!session_.get()) {
- // The first we need to make sure capture and encode thread are
- // running.
- capture_thread_.Start();
- encode_thread_.Start();
-
// Then we create a SessionManager passing the message loops that
// it should run on.
- // Note that we pass the ownership of the capturer and encoder to
- // the session manager.
DCHECK(capturer_.get());
DCHECK(encoder_.get());
- session_ = new SessionManager(capture_thread_.message_loop(),
- encode_thread_.message_loop(),
- message_loop(),
- capturer_.release(),
- encoder_.release());
+ session_ = new SessionManager(context_->capture_message_loop(),
+ context_->encode_message_loop(),
+ context_->main_message_loop(),
+ capturer_.get(),
+ encoder_.get());
// Immediately add the client and start the session.
session_->AddClient(client);
@@ -128,26 +74,24 @@ void ChromotingHost::OnClientConnected(ClientConnection* client) {
}
void ChromotingHost::OnClientDisconnected(ClientConnection* client) {
- DCHECK_EQ(message_loop(), MessageLoop::current());
+ DCHECK_EQ(context_->main_message_loop(), MessageLoop::current());
- // Remove the client from the session manager.
- if (session_.get())
+ // Remove the client from the session manager and pause the session.
+ // TODO(hclam): Pause only if the last client disconnected.
+ if (session_.get()) {
session_->RemoveClient(client);
+ session_->Pause();
+ }
// Also remove reference to ClientConnection from this object.
client_ = NULL;
-
- // TODO(hclam): If the last client has disconnected we need to destroy
- // the session manager and shutdown the capture and encode threads.
- // Right now we assume that there's only one client.
- DestroySession();
}
////////////////////////////////////////////////////////////////////////////
// ClientConnection::EventHandler implementations
void ChromotingHost::HandleMessages(ClientConnection* client,
ClientMessageList* messages) {
- DCHECK_EQ(message_loop(), MessageLoop::current());
+ DCHECK_EQ(context_->main_message_loop(), MessageLoop::current());
// Delegate the messages to EventExecutor and delete the unhandled
// messages.
@@ -157,7 +101,7 @@ void ChromotingHost::HandleMessages(ClientConnection* client,
}
void ChromotingHost::OnConnectionOpened(ClientConnection* client) {
- DCHECK_EQ(message_loop(), MessageLoop::current());
+ DCHECK_EQ(context_->main_message_loop(), MessageLoop::current());
// Completes the client connection.
LOG(INFO) << "Connection to client established.";
@@ -165,7 +109,7 @@ void ChromotingHost::OnConnectionOpened(ClientConnection* client) {
}
void ChromotingHost::OnConnectionClosed(ClientConnection* client) {
- DCHECK_EQ(message_loop(), MessageLoop::current());
+ DCHECK_EQ(context_->main_message_loop(), MessageLoop::current());
// Completes the client connection.
LOG(INFO) << "Connection to client closed.";
@@ -173,7 +117,7 @@ void ChromotingHost::OnConnectionClosed(ClientConnection* client) {
}
void ChromotingHost::OnConnectionFailed(ClientConnection* client) {
- DCHECK_EQ(message_loop(), MessageLoop::current());
+ DCHECK_EQ(context_->main_message_loop(), MessageLoop::current());
// The client has disconnected.
LOG(ERROR) << "Connection failed unexpectedly.";
@@ -183,10 +127,9 @@ void ChromotingHost::OnConnectionFailed(ClientConnection* client) {
////////////////////////////////////////////////////////////////////////////
// JingleClient::Callback implementations
void ChromotingHost::OnStateChange(JingleClient* jingle_client,
- JingleClient::State state) {
- DCHECK_EQ(jingle_client_.get(), jingle_client);
-
+ JingleClient::State state) {
if (state == JingleClient::CONNECTED) {
+ DCHECK_EQ(jingle_client_.get(), jingle_client);
LOG(INFO) << "Host connected as "
<< jingle_client->GetFullJid() << "." << std::endl;
@@ -197,17 +140,21 @@ void ChromotingHost::OnStateChange(JingleClient* jingle_client,
LOG(INFO) << "Host disconnected from talk network." << std::endl;
heartbeat_sender_ = NULL;
- // Quit the message loop if disconected.
// TODO(sergeyu): We should try reconnecting here instead of terminating
// the host.
- message_loop()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
- host_done_->Signal();
+ // Post a shutdown task to properly shutdown the chromoting host.
+ context_->main_message_loop()->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &ChromotingHost::DoShutdown));
}
}
bool ChromotingHost::OnAcceptConnection(
JingleClient* jingle_client, const std::string& jid,
JingleChannel::Callback** channel_callback) {
+ AutoLock auto_lock(lock_);
+ if (state_ != kStarted)
+ return false;
+
DCHECK_EQ(jingle_client_.get(), jingle_client);
// TODO(hclam): Allow multiple clients to connect to the host.
@@ -218,13 +165,18 @@ bool ChromotingHost::OnAcceptConnection(
// If we accept the connected then create a client object and set the
// callback.
- client_ = new ClientConnection(message_loop(), new ProtocolDecoder(), this);
+ client_ = new ClientConnection(context_->main_message_loop(),
+ new ProtocolDecoder(), this);
*channel_callback = client_.get();
return true;
}
void ChromotingHost::OnNewConnection(JingleClient* jingle_client,
- scoped_refptr<JingleChannel> channel) {
+ scoped_refptr<JingleChannel> channel) {
+ AutoLock auto_lock(lock_);
+ if (state_ != kStarted)
+ return;
+
DCHECK_EQ(jingle_client_.get(), jingle_client);
// Since the session manager has not started, it is still safe to access
@@ -233,8 +185,67 @@ void ChromotingHost::OnNewConnection(JingleClient* jingle_client,
client_->set_jingle_channel(channel);
}
-MessageLoop* ChromotingHost::message_loop() {
- return main_thread_.message_loop();
+void ChromotingHost::DoStart(Task* shutdown_task) {
+ DCHECK_EQ(context_->main_message_loop(), MessageLoop::current());
+ DCHECK(!jingle_client_);
+ DCHECK(shutdown_task);
+
+ // Make sure this object is not started.
+ {
+ AutoLock auto_lock(lock_);
+ if (state_ != kInitial)
+ return;
+ state_ = kStarted;
+ }
+
+ // Save the shutdown task.
+ shutdown_task_.reset(shutdown_task);
+
+ std::string xmpp_login;
+ std::string xmpp_auth_token;
+ if (!config_->GetString(kXmppLoginConfigPath, &xmpp_login) ||
+ !config_->GetString(kXmppAuthTokenConfigPath, &xmpp_auth_token)) {
+ LOG(ERROR) << "XMMP credentials are not defined in config.";
+ return;
+ }
+
+ // Connect to the talk network with a JingleClient.
+ jingle_client_ = new JingleClient(context_->jingle_thread());
+ jingle_client_->Init(xmpp_login, xmpp_auth_token,
+ kChromotingTokenServiceName, this);
+}
+
+void ChromotingHost::DoShutdown() {
+ DCHECK_EQ(context_->main_message_loop(), MessageLoop::current());
+
+ // No-op if this object is not started yet.
+ {
+ AutoLock auto_lock(lock_);
+ if (state_ != kStarted)
+ return;
+ state_ = kStopped;
+ }
+
+ // Tell the session to pause and then disconnect all clients.
+ if (session_.get()) {
+ session_->Pause();
+ session_->RemoveAllClients();
+ }
+
+ // Disconnect all clients.
+ if (client_) {
+ client_->Disconnect();
+ }
+
+ // Disconnect from the talk network.
+ if (jingle_client_) {
+ jingle_client_->Close();
+ }
+
+ // Lastly call the shutdown task.
+ if (shutdown_task_.get()) {
+ shutdown_task_->Run();
+ }
}
} // namespace remoting
diff --git a/remoting/host/chromoting_host.h b/remoting/host/chromoting_host.h
index 930bf89..fd07791 100644
--- a/remoting/host/chromoting_host.h
+++ b/remoting/host/chromoting_host.h
@@ -17,12 +17,11 @@
#include "remoting/jingle_glue/jingle_client.h"
#include "remoting/jingle_glue/jingle_thread.h"
-namespace base {
-class WaitableEvent;
-} // namespace base
+class Task;
namespace remoting {
+class ChromotingHostContext;
class MutableHostConfig;
// A class to implement the functionality of a host process.
@@ -51,24 +50,24 @@ class MutableHostConfig;
// return to the idle state. We then go to step (2) if there a new
// incoming connection.
class ChromotingHost : public base::RefCountedThreadSafe<ChromotingHost>,
- public ClientConnection::EventHandler,
- public JingleClient::Callback {
+ public ClientConnection::EventHandler,
+ public JingleClient::Callback {
public:
- ChromotingHost(MutableHostConfig* config, Capturer* capturer,
- Encoder* encoder, EventExecutor* executor,
- base::WaitableEvent* host_done);
+ ChromotingHost(ChromotingHostContext* context, MutableHostConfig* config,
+ Capturer* capturer, Encoder* encoder, EventExecutor* executor);
virtual ~ChromotingHost();
- // Run the host porcess. This method returns only after the message loop
- // of the host process exits.
- void Run();
+ // Start the host porcess. This methods starts the chromoting host
+ // asynchronously.
+ //
+ // |shutdown_task| is called if Start() has failed ot Shutdown() is called
+ // and all related operations are completed.
+ //
+ // This method can only be called once during the lifetime of this object.
+ void Start(Task* shutdown_task);
// This method is called when we need to the host process.
- void DestroySession();
-
- // This method talks to the cloud to register the host process. If
- // successful we will start listening to network requests.
- void RegisterHost();
+ void Shutdown();
// This method is called if a client is connected to this object.
void OnClientConnected(ClientConnection* client);
@@ -95,20 +94,21 @@ class ChromotingHost : public base::RefCountedThreadSafe<ChromotingHost>,
scoped_refptr<JingleChannel> channel);
private:
- // The message loop that this class runs on.
- MessageLoop* message_loop();
-
- // The main thread that this object runs on.
- base::Thread main_thread_;
+ enum State {
+ kInitial,
+ kStarted,
+ kStopped,
+ };
- // Used to handle the Jingle connection.
- JingleThread network_thread_;
+ // This method connects to the talk network and start listening for incoming
+ // connections.
+ void DoStart(Task* shutdown_task);
- // A thread that hosts capture operations.
- base::Thread capture_thread_;
+ // This method shuts down the host process.
+ void DoShutdown();
- // A thread that hosts encode operations.
- base::Thread encode_thread_;
+ // The context that the chromoting host runs on.
+ ChromotingHostContext* context_;
scoped_refptr<MutableHostConfig> config_;
@@ -137,8 +137,17 @@ class ChromotingHost : public base::RefCountedThreadSafe<ChromotingHost>,
// Session manager for the host process.
scoped_refptr<SessionManager> session_;
- // Signals the host is ready to be destroyed.
- base::WaitableEvent* host_done_;
+ // This task gets executed when this object fails to connect to the
+ // talk network or Shutdown() is called.
+ scoped_ptr<Task> shutdown_task_;
+
+ // Tracks the internal state of the host.
+ // This variable is written on the main thread of ChromotingHostContext
+ // and read by jingle thread.
+ State state_;
+
+ // Lock is to lock the access to |state_|.
+ Lock lock_;
DISALLOW_COPY_AND_ASSIGN(ChromotingHost);
};
diff --git a/remoting/host/chromoting_host_context.cc b/remoting/host/chromoting_host_context.cc
new file mode 100644
index 0000000..2f889a1
--- /dev/null
+++ b/remoting/host/chromoting_host_context.cc
@@ -0,0 +1,56 @@
+// 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/host/chromoting_host_context.h"
+
+#include <string>
+
+#include "base/thread.h"
+#include "remoting/jingle_glue/jingle_thread.h"
+
+namespace remoting {
+
+ChromotingHostContext::ChromotingHostContext()
+ : main_thread_("ChromotingMainThread"),
+ capture_thread_("ChromotingCaptureThread"),
+ encode_thread_("ChromotingEncodeThread") {
+}
+
+ChromotingHostContext::~ChromotingHostContext() {
+}
+
+void ChromotingHostContext::Start() {
+ // Start all the threads.
+ main_thread_.StartWithOptions(
+ base::Thread::Options(MessageLoop::TYPE_UI, 0));
+ capture_thread_.Start();
+ encode_thread_.Start();
+ jingle_thread_.Start();
+}
+
+void ChromotingHostContext::Stop() {
+ // Stop all the threads.
+ jingle_thread_.Stop();
+ encode_thread_.Stop();
+ capture_thread_.Stop();
+ main_thread_.Stop();
+}
+
+JingleThread* ChromotingHostContext::jingle_thread() {
+ return &jingle_thread_;
+}
+
+MessageLoop* ChromotingHostContext::main_message_loop() {
+ return main_thread_.message_loop();
+}
+
+MessageLoop* ChromotingHostContext::capture_message_loop() {
+ return capture_thread_.message_loop();
+}
+
+MessageLoop* ChromotingHostContext::encode_message_loop() {
+ return encode_thread_.message_loop();
+}
+
+} // namespace remoting
diff --git a/remoting/host/chromoting_host_context.h b/remoting/host/chromoting_host_context.h
new file mode 100644
index 0000000..e336c1e
--- /dev/null
+++ b/remoting/host/chromoting_host_context.h
@@ -0,0 +1,51 @@
+// 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.
+
+#ifndef REMOTING_CHROMOTING_HOST_CONTEXT_H_
+#define REMOTING_CHROMOTING_HOST_CONTEXT_H_
+
+#include <string>
+
+#include "base/thread.h"
+#include "remoting/jingle_glue/jingle_thread.h"
+#include "testing/gtest/include/gtest/gtest_prod.h"
+
+namespace remoting {
+
+// A class that manages threads and running context for the chromoting host
+// process.
+class ChromotingHostContext {
+ public:
+ ChromotingHostContext();
+ virtual ~ChromotingHostContext();
+
+ virtual void Start();
+ virtual void Stop();
+
+ virtual JingleThread* jingle_thread();
+ virtual MessageLoop* main_message_loop();
+ virtual MessageLoop* capture_message_loop();
+ virtual MessageLoop* encode_message_loop();
+
+ private:
+ FRIEND_TEST(ChromotingHostContextTest, StartAndStop);
+
+ // A thread that host network operations.
+ JingleThread jingle_thread_;
+
+ // A thread that host ChromotingHost.
+ base::Thread main_thread_;
+
+ // A thread that host all capture operations.
+ base::Thread capture_thread_;
+
+ // A thread that host all encode operations.
+ base::Thread encode_thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChromotingHostContext);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_HOST_CHROMOTING_HOST_CONTEXT_H_
diff --git a/remoting/host/chromoting_host_context_unittest.cc b/remoting/host/chromoting_host_context_unittest.cc
new file mode 100644
index 0000000..6b481d7
--- /dev/null
+++ b/remoting/host/chromoting_host_context_unittest.cc
@@ -0,0 +1,28 @@
+// 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 "base/message_loop.h"
+#include "remoting/host/chromoting_host_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace remoting {
+
+// A simple test that starts and stop the context. This tests the context
+// operates properly and all threads and message loops are valid.
+TEST(ChromotingHostContextTest, StartAndStop) {
+ ChromotingHostContext context;
+ context.Start();
+ EXPECT_TRUE(context.jingle_thread());
+ EXPECT_TRUE(context.main_message_loop());
+ EXPECT_TRUE(context.capture_message_loop());
+ EXPECT_TRUE(context.encode_message_loop());
+ context.Stop();
+
+ // Expect all the threads are stopped.
+ EXPECT_FALSE(context.main_thread_.IsRunning());
+ EXPECT_FALSE(context.capture_thread_.IsRunning());
+ EXPECT_FALSE(context.encode_thread_.IsRunning());
+}
+
+} // namespace remoting
diff --git a/remoting/host/client_connection.cc b/remoting/host/client_connection.cc
index d2c0df0..d69a326 100644
--- a/remoting/host/client_connection.cc
+++ b/remoting/host/client_connection.cc
@@ -38,7 +38,10 @@ ClientConnection::~ClientConnection() {
void ClientConnection::SendInitClientMessage(int width, int height) {
DCHECK_EQ(loop_, MessageLoop::current());
DCHECK(!update_stream_size_);
- DCHECK(channel_.get());
+
+ // If we are disconnected then return.
+ if (!channel_)
+ return;
HostMessage msg;
msg.mutable_init_client()->set_width(width);
@@ -49,7 +52,10 @@ void ClientConnection::SendInitClientMessage(int width, int height) {
void ClientConnection::SendBeginUpdateStreamMessage() {
DCHECK_EQ(loop_, MessageLoop::current());
- DCHECK(channel_.get());
+
+ // If we are disconnected then return.
+ if (!channel_)
+ return;
HostMessage msg;
msg.mutable_begin_update_stream();
@@ -65,7 +71,10 @@ void ClientConnection::SendUpdateStreamPacketMessage(
const UpdateStreamPacketHeader* header,
scoped_refptr<DataBuffer> data) {
DCHECK_EQ(loop_, MessageLoop::current());
- DCHECK(channel_.get());
+
+ // If we are disconnected then return.
+ if (!channel_)
+ return;
HostMessage msg;
msg.mutable_update_stream_packet()->mutable_header()->CopyFrom(*header);
@@ -81,7 +90,10 @@ void ClientConnection::SendUpdateStreamPacketMessage(
void ClientConnection::SendEndUpdateStreamMessage() {
DCHECK_EQ(loop_, MessageLoop::current());
- DCHECK(channel_.get());
+
+ // If we are disconnected then return.
+ if (!channel_)
+ return;
HostMessage msg;
msg.mutable_end_update_stream();
@@ -116,8 +128,11 @@ int ClientConnection::GetPendingUpdateStreamMessages() {
void ClientConnection::Disconnect() {
DCHECK_EQ(loop_, MessageLoop::current());
- DCHECK(channel_.get());
- channel_->Close();
+ // If there is a channel then close it and release the reference.
+ if (channel_) {
+ channel_->Close();
+ channel_ = NULL;
+ }
}
void ClientConnection::OnStateChange(JingleChannel* channel,
diff --git a/remoting/host/client_connection.h b/remoting/host/client_connection.h
index 810eb61..3e85cc9 100644
--- a/remoting/host/client_connection.h
+++ b/remoting/host/client_connection.h
@@ -90,7 +90,10 @@ class ClientConnection : public base::RefCountedThreadSafe<ClientConnection>,
// TODO(hclam): Report this number accurately.
virtual int GetPendingUpdateStreamMessages();
- // Disconnect the remote viewer.
+ // Disconnect the client connection. This method is allowed to be called
+ // more than once and calls after the first one will be ignored.
+ //
+ // After this method is called all the send method calls will be ignored.
virtual void Disconnect();
/////////////////////////////////////////////////////////////////////////////
diff --git a/remoting/host/client_connection_unittest.cc b/remoting/host/client_connection_unittest.cc
index f82c887..b7cb10c 100644
--- a/remoting/host/client_connection_unittest.cc
+++ b/remoting/host/client_connection_unittest.cc
@@ -12,6 +12,7 @@
using ::testing::_;
using ::testing::NotNull;
+using ::testing::StrictMock;
namespace remoting {
@@ -23,13 +24,13 @@ class ClientConnectionTest : public testing::Test {
protected:
virtual void SetUp() {
decoder_ = new MockProtocolDecoder();
- channel_ = new MockJingleChannel();
+ channel_ = new StrictMock<MockJingleChannel>();
// Allocate a ClientConnection object with the mock objects. we give the
// ownership of decoder to the viewer.
viewer_ = new ClientConnection(&message_loop_,
- decoder_,
- &handler_);
+ decoder_,
+ &handler_);
viewer_->set_jingle_channel(channel_.get());
}
@@ -37,9 +38,12 @@ class ClientConnectionTest : public testing::Test {
MessageLoop message_loop_;
MockProtocolDecoder* decoder_;
MockClientConnectionEventHandler handler_;
- scoped_refptr<MockJingleChannel> channel_;
scoped_refptr<ClientConnection> viewer_;
+ // |channel_| is wrapped with StrictMock because we limit strictly the calls
+ // to it.
+ scoped_refptr<StrictMock<MockJingleChannel> > channel_;
+
private:
DISALLOW_COPY_AND_ASSIGN(ClientConnectionTest);
};
@@ -95,4 +99,23 @@ TEST_F(ClientConnectionTest, ParseMessages) {
message_loop_.RunAllPending();
}
+// Test that we can close client connection more than once and operations
+// after the connection is closed has no effect.
+TEST_F(ClientConnectionTest, Close) {
+ EXPECT_CALL(*channel_, Close());
+ viewer_->Disconnect();
+
+ viewer_->SendBeginUpdateStreamMessage();
+ scoped_ptr<UpdateStreamPacketHeader> header(new UpdateStreamPacketHeader);
+ header->set_x(0);
+ header->set_y(0);
+ header->set_width(640);
+ header->set_height(480);
+
+ scoped_refptr<media::DataBuffer> data = new media::DataBuffer(10);
+ viewer_->SendUpdateStreamPacketMessage(header.get(), data);
+ viewer_->SendEndUpdateStreamMessage();
+ viewer_->Disconnect();
+}
+
} // namespace remoting
diff --git a/remoting/host/encoder_verbatim.cc b/remoting/host/encoder_verbatim.cc
index 3151c72..ce0d405 100644
--- a/remoting/host/encoder_verbatim.cc
+++ b/remoting/host/encoder_verbatim.cc
@@ -30,7 +30,8 @@ void EncoderVerbatim::Encode(const DirtyRects& dirty_rects,
EncodingState state = EncodingInProgress;
if (i == 0) {
state |= EncodingStarting;
- } else if (i == num_rects - 1) {
+ }
+ if (i == num_rects - 1) {
state |= EncodingEnded;
}
data_available_callback->Run(header.release(),
diff --git a/remoting/host/session_manager.cc b/remoting/host/session_manager.cc
index cd6fc47..1e04482 100644
--- a/remoting/host/session_manager.cc
+++ b/remoting/host/session_manager.cc
@@ -139,6 +139,12 @@ void SessionManager::RemoveClient(scoped_refptr<ClientConnection> client) {
NewRunnableMethod(this, &SessionManager::DoRemoveClient, client));
}
+void SessionManager::RemoveAllClients() {
+ network_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &SessionManager::DoRemoveAllClients));
+}
+
void SessionManager::DoCapture() {
DCHECK_EQ(capture_loop_, MessageLoop::current());
@@ -166,8 +172,7 @@ void SessionManager::DoCapture() {
ScheduleNextCapture();
// And finally perform one capture.
- DCHECK(capturer_.get());
- capturer_->CaptureDirtyRects(
+ capturer()->CaptureDirtyRects(
NewRunnableMethod(this, &SessionManager::CaptureDoneTask));
}
@@ -190,13 +195,11 @@ void SessionManager::DoEncode(const CaptureData *capture_data) {
DCHECK_EQ(encode_loop_, MessageLoop::current());
- DCHECK(encoder_.get());
-
// TODO(hclam): Enable |force_refresh| if a new client was
// added.
- encoder_->SetSize(capture_data->width_, capture_data->height_);
- encoder_->SetPixelFormat(capture_data->pixel_format_);
- encoder_->Encode(
+ encoder()->SetSize(capture_data->width_, capture_data->height_);
+ encoder()->SetPixelFormat(capture_data->pixel_format_);
+ encoder()->Encode(
capture_data->dirty_rects_,
capture_data->data_,
capture_data->data_strides_,
@@ -239,7 +242,7 @@ void SessionManager::DoGetInitInfo(scoped_refptr<ClientConnection> client) {
network_loop_->PostTask(
FROM_HERE,
NewRunnableMethod(this, &SessionManager::DoSendInit, client,
- capturer_->GetWidth(), capturer_->GetHeight()));
+ capturer()->GetWidth(), capturer()->GetHeight()));
// And then add the client to the list so it can receive update stream.
// It is important we do so in such order or the client will receive
@@ -288,8 +291,16 @@ void SessionManager::DoRemoveClient(scoped_refptr<ClientConnection> client) {
// TODO(hclam): Is it correct to do to a scoped_refptr?
ClientConnectionList::iterator it
= std::find(clients_.begin(), clients_.end(), client);
- if (it != clients_.end())
+ if (it != clients_.end()) {
clients_.erase(it);
+ }
+}
+
+void SessionManager::DoRemoveAllClients() {
+ DCHECK_EQ(network_loop_, MessageLoop::current());
+
+ // Clear the list of clients.
+ clients_.clear();
}
void SessionManager::DoRateControl() {
@@ -354,12 +365,12 @@ void SessionManager::CaptureDoneTask() {
scoped_ptr<CaptureData> data(new CaptureData);
// Save results of the capture.
- capturer_->GetData(data->data_);
- capturer_->GetDataStride(data->data_strides_);
- capturer_->GetDirtyRects(&data->dirty_rects_);
- data->pixel_format_ = capturer_->GetPixelFormat();
- data->width_ = capturer_->GetWidth();
- data->height_ = capturer_->GetHeight();
+ capturer()->GetData(data->data_);
+ capturer()->GetDataStride(data->data_strides_);
+ capturer()->GetDirtyRects(&data->dirty_rects_);
+ data->pixel_format_ = capturer()->GetPixelFormat();
+ data->width_ = capturer()->GetWidth();
+ data->height_ = capturer()->GetHeight();
encode_loop_->PostTask(
FROM_HERE,
@@ -384,10 +395,20 @@ void SessionManager::EncodeDataAvailableTask(
data,
state));
- if (state == Encoder::EncodingEnded) {
+ if (state & Encoder::EncodingEnded) {
capture_loop_->PostTask(
FROM_HERE, NewRunnableMethod(this, &SessionManager::DoFinishEncode));
}
}
+Capturer* SessionManager::capturer() {
+ DCHECK_EQ(capture_loop_, MessageLoop::current());
+ return capturer_.get();
+}
+
+Encoder* SessionManager::encoder() {
+ DCHECK_EQ(encode_loop_, MessageLoop::current());
+ return encoder_.get();
+}
+
} // namespace remoting
diff --git a/remoting/host/session_manager.h b/remoting/host/session_manager.h
index 9c8ad50..d624a35 100644
--- a/remoting/host/session_manager.h
+++ b/remoting/host/session_manager.h
@@ -67,7 +67,7 @@ class SessionManager : public base::RefCountedThreadSafe<SessionManager> {
public:
// Construct a SessionManager. Message loops and threads are provided.
- // Ownership of Capturer and Encoder are given to this object.
+ // This object does not own capturer and encoder.
SessionManager(MessageLoop* capture_loop,
MessageLoop* encode_loop,
MessageLoop* network_loop,
@@ -95,6 +95,9 @@ class SessionManager : public base::RefCountedThreadSafe<SessionManager> {
// Remove a client from receiving screen updates.
void RemoveClient(scoped_refptr<ClientConnection> client);
+ // Remove all clients.
+ void RemoveAllClients();
+
private:
// Stores the data and information of a capture to pass off to the
@@ -130,6 +133,7 @@ class SessionManager : public base::RefCountedThreadSafe<SessionManager> {
void DoSetMaxRate(double max_rate);
void DoAddClient(scoped_refptr<ClientConnection> client);
void DoRemoveClient(scoped_refptr<ClientConnection> client);
+ void DoRemoveAllClients();
void DoRateControl();
// Hepler method to schedule next capture using the current rate.
@@ -145,6 +149,10 @@ class SessionManager : public base::RefCountedThreadSafe<SessionManager> {
const scoped_refptr<media::DataBuffer>& data,
Encoder::EncodingState state);
+ // Getters for capturer and encoder.
+ Capturer* capturer();
+ Encoder* encoder();
+
// Message loops used by this class.
MessageLoop* capture_loop_;
MessageLoop* encode_loop_;
diff --git a/remoting/host/simple_host_process.cc b/remoting/host/simple_host_process.cc
index 0f12c43..8697b3d 100644
--- a/remoting/host/simple_host_process.cc
+++ b/remoting/host/simple_host_process.cc
@@ -27,6 +27,7 @@
#include "base/waitable_event.h"
#include "remoting/host/capturer_fake.h"
#include "remoting/host/chromoting_host.h"
+#include "remoting/host/chromoting_host_context.h"
#include "remoting/host/encoder_verbatim.h"
#include "remoting/host/json_host_config.h"
@@ -52,6 +53,10 @@ const char kHomePath[] = "HOME";
static char* GetEnvironmentVar(const char* x) { return getenv(x); }
#endif
+void ShutdownTask(MessageLoop* message_loop) {
+ message_loop->PostTask(FROM_HERE, new MessageLoop::QuitTask());
+}
+
const std::string kFakeSwitchName = "fake";
const std::string kConfigSwitchName = "config";
@@ -96,6 +101,7 @@ int main(int argc, char** argv) {
if (fake) {
// Inject a fake capturer.
+ LOG(INFO) << "Usage a fake capturer.";
capturer.reset(new remoting::CapturerFake());
}
@@ -111,16 +117,25 @@ int main(int argc, char** argv) {
return 1;
}
- base::WaitableEvent host_done(false, false);
+ // Allocate a chromoting context and starts it.
+ remoting::ChromotingHostContext context;
+ context.Start();
+
+ // Construct a chromoting host.
scoped_refptr<remoting::ChromotingHost> host =
- new remoting::ChromotingHost(config,
+ new remoting::ChromotingHost(&context,
+ config,
capturer.release(),
encoder.release(),
- executor.release(),
- &host_done);
- host->Run();
- host_done.Wait();
+ executor.release());
+
+ // Let the chromoting host runs until the shutdown task is executed.
+ MessageLoop message_loop;
+ host->Start(NewRunnableFunction(&ShutdownTask, &message_loop));
+ message_loop.Run();
+ // And then stop the chromoting context.
+ context.Stop();
file_io_thread.Stop();
return 0;
}
diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp
index 1e037ef..341f10d 100644
--- a/remoting/remoting.gyp
+++ b/remoting/remoting.gyp
@@ -145,6 +145,8 @@
'host/capturer.h',
'host/chromoting_host.cc',
'host/chromoting_host.h',
+ 'host/chromoting_host_context.cc',
+ 'host/chromoting_hsot_context.h',
'host/client_connection.cc',
'host/client_connection.h',
'host/differ.h',
@@ -323,6 +325,7 @@
'base/protocol_decoder_unittest.cc',
'client/mock_objects.h',
'client/decoder_verbatim_unittest.cc',
+ 'host/chromoting_host_context_unittest.cc',
'host/differ_unittest.cc',
'host/differ_block_unittest.cc',
'host/json_host_config_unittest.cc',