1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "remoting/test/app_remoting_connection_helper.h"
7 #include "base/callback_helpers.h"
8 #include "base/json/json_reader.h"
9 #include "base/logging.h"
10 #include "base/run_loop.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "base/timer/timer.h"
13 #include "base/values.h"
14 #include "remoting/protocol/host_stub.h"
15 #include "remoting/test/app_remoting_test_driver_environment.h"
16 #include "remoting/test/remote_application_details.h"
17 #include "remoting/test/test_chromoting_client.h"
20 const int kDefaultDPI
= 96;
21 const int kDefaultWidth
= 1024;
22 const int kDefaultHeight
= 768;
24 const char kHostProcessWindowTitle
[] = "Host Process";
30 AppRemotingConnectionHelper::AppRemotingConnectionHelper(
31 const RemoteApplicationDetails
& application_details
)
32 : application_details_(application_details
),
33 connection_is_ready_for_tests_(false),
34 timer_(new base::Timer(true, false)) {
37 AppRemotingConnectionHelper::~AppRemotingConnectionHelper() {
38 // |client_| destroys some of its members via DeleteSoon on the message loop's
39 // TaskRunner so we need to run the loop until it has no more work to do.
40 client_
->RemoveRemoteConnectionObserver(this);
43 base::RunLoop().RunUntilIdle();
46 void AppRemotingConnectionHelper::Initialize(
47 scoped_ptr
<TestChromotingClient
> test_chromoting_client
) {
48 client_
= test_chromoting_client
.Pass();
49 client_
->AddRemoteConnectionObserver(this);
52 void AppRemotingConnectionHelper::SetHostMessageReceivedCallback(
53 HostMessageReceivedCallback host_message_received_callback
) {
54 host_message_received_callback_
= host_message_received_callback
;
57 bool AppRemotingConnectionHelper::StartConnection() {
58 DCHECK(thread_checker_
.CalledOnValidThread());
61 RemoteHostInfo remote_host_info
;
62 remoting::test::AppRemotingSharedData
->GetRemoteHostInfoForApplicationId(
63 application_details_
.application_id
, &remote_host_info
);
65 if (!remote_host_info
.IsReadyForConnection()) {
68 remoting::test::AppRemotingSharedData
->AddHostToReleaseList(
69 application_details_
.application_id
, remote_host_info
.host_id
);
71 DCHECK(!run_loop_
|| !run_loop_
->running());
72 run_loop_
.reset(new base::RunLoop());
74 // We will wait up to 30 seconds to complete the remote connection and for the
75 // main application window to become visible.
76 DCHECK(!timer_
->IsRunning());
77 timer_
->Start(FROM_HERE
, base::TimeDelta::FromSeconds(30),
78 run_loop_
->QuitClosure());
80 client_
->StartConnection(remote_host_info
.GenerateConnectionSetupInfo(
81 AppRemotingSharedData
->access_token(),
82 AppRemotingSharedData
->user_name()));
87 if (connection_is_ready_for_tests_
) {
90 client_
->EndConnection();
95 protocol::ClipboardStub
* AppRemotingConnectionHelper::clipboard_forwarder() {
96 return client_
->clipboard_forwarder();
99 protocol::HostStub
* AppRemotingConnectionHelper::host_stub() {
100 return client_
->host_stub();
103 protocol::InputStub
* AppRemotingConnectionHelper::input_stub() {
104 return client_
->input_stub();
107 void AppRemotingConnectionHelper::ConnectionStateChanged(
108 protocol::ConnectionToHost::State state
,
109 protocol::ErrorCode error_code
) {
110 DCHECK(thread_checker_
.CalledOnValidThread());
112 // If the connection is closed or failed then mark the connection as closed
113 // and quit the current RunLoop if it exists.
114 if (state
== protocol::ConnectionToHost::CLOSED
||
115 state
== protocol::ConnectionToHost::FAILED
||
116 error_code
!= protocol::OK
) {
117 connection_is_ready_for_tests_
= false;
125 void AppRemotingConnectionHelper::ConnectionReady(bool ready
) {
126 DCHECK(thread_checker_
.CalledOnValidThread());
129 SendClientConnectionDetailsToHost();
131 // We will only get called here with a false value for |ready| if the video
132 // renderer encounters an error.
133 connection_is_ready_for_tests_
= false;
141 void AppRemotingConnectionHelper::HostMessageReceived(
142 const protocol::ExtensionMessage
& message
) {
143 DCHECK(thread_checker_
.CalledOnValidThread());
145 VLOG(2) << "HostMessage received by HostMessageReceived()."
146 << " type: " << message
.type() << " data: " << message
.data();
148 // If a callback is not registered, then the message is passed to a default
149 // handler for the class based on the message type.
150 if (!host_message_received_callback_
.is_null()) {
151 base::ResetAndReturn(&host_message_received_callback_
).Run(message
);
152 } else if (message
.type() == "onWindowAdded") {
153 HandleOnWindowAddedMessage(message
);
155 VLOG(2) << "HostMessage not handled by HostMessageReceived().";
159 void AppRemotingConnectionHelper::SendClientConnectionDetailsToHost() {
160 // First send an access token which will be used for Google Drive access.
161 protocol::ExtensionMessage message
;
162 message
.set_type("accessToken");
163 message
.set_data(AppRemotingSharedData
->access_token());
165 VLOG(1) << "Sending access token to host";
166 client_
->host_stub()->DeliverClientMessage(message
);
168 // Next send the host a description of the client screen size.
169 protocol::ClientResolution client_resolution
;
170 client_resolution
.set_width(kDefaultWidth
);
171 client_resolution
.set_height(kDefaultHeight
);
172 client_resolution
.set_x_dpi(kDefaultDPI
);
173 client_resolution
.set_y_dpi(kDefaultDPI
);
174 client_resolution
.set_dips_width(kDefaultWidth
);
175 client_resolution
.set_dips_height(kDefaultHeight
);
177 VLOG(1) << "Sending ClientResolution details to host";
178 client_
->host_stub()->NotifyClientResolution(client_resolution
);
180 // Finally send a message to start sending us video packets.
181 protocol::VideoControl video_control
;
182 video_control
.set_enable(true);
184 VLOG(1) << "Sending enable VideoControl message to host";
185 client_
->host_stub()->ControlVideo(video_control
);
188 void AppRemotingConnectionHelper::HandleOnWindowAddedMessage(
189 const remoting::protocol::ExtensionMessage
& message
) {
190 DCHECK_EQ(message
.type(), "onWindowAdded");
192 const base::DictionaryValue
* message_data
= nullptr;
193 scoped_ptr
<base::Value
> host_message
= base::JSONReader::Read(message
.data());
194 if (!host_message
.get() || !host_message
->GetAsDictionary(&message_data
)) {
195 LOG(ERROR
) << "onWindowAdded message received was not valid JSON.";
202 std::string current_window_title
;
203 message_data
->GetString("title", ¤t_window_title
);
204 if (current_window_title
== kHostProcessWindowTitle
) {
205 LOG(ERROR
) << "Host Process Window is visible, this likely means that the "
206 << "underlying application is in a bad state, YMMV.";
209 std::string main_window_title
= application_details_
.main_window_title
;
210 if (current_window_title
.find_first_of(main_window_title
) == 0) {
211 connection_is_ready_for_tests_
= true;
213 if (timer_
->IsRunning()) {
218 // Now that the main window is visible, give the app some time to settle
219 // before signaling that it is ready to run tests.
220 timer_
->Start(FROM_HERE
, base::TimeDelta::FromSeconds(2),
221 run_loop_
->QuitClosure());
226 } // namespace remoting