1// Copyright 2013 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.
4
5#include "remoting/host/setup/daemon_controller.h"
6
7#include "base/bind.h"
8#include "base/location.h"
9#include "base/single_thread_task_runner.h"
10#include "base/thread_task_runner_handle.h"
11#include "base/values.h"
12#include "remoting/base/auto_thread.h"
13#include "remoting/base/auto_thread_task_runner.h"
14
15namespace remoting {
16
17// Name of the Daemon Controller's worker thread.
18const char kDaemonControllerThreadName[] = "Daemon Controller thread";
19
20DaemonController::DaemonController(scoped_ptr<Delegate> delegate)
21    : caller_task_runner_(base::ThreadTaskRunnerHandle::Get()),
22      delegate_(delegate.Pass()) {
23  // Launch the delegate thread.
24  delegate_thread_.reset(new AutoThread(kDaemonControllerThreadName));
25#if defined(OS_WIN)
26  delegate_thread_->SetComInitType(AutoThread::COM_INIT_STA);
27  delegate_task_runner_ =
28      delegate_thread_->StartWithType(base::MessageLoop::TYPE_UI);
29#else
30  delegate_task_runner_ =
31      delegate_thread_->StartWithType(base::MessageLoop::TYPE_DEFAULT);
32#endif
33}
34
35DaemonController::State DaemonController::GetState() {
36  DCHECK(caller_task_runner_->BelongsToCurrentThread());
37  return delegate_->GetState();
38}
39
40void DaemonController::GetConfig(const GetConfigCallback& done) {
41  DCHECK(caller_task_runner_->BelongsToCurrentThread());
42
43  DaemonController::GetConfigCallback wrapped_done = base::Bind(
44      &DaemonController::InvokeConfigCallbackAndScheduleNext, this, done);
45  base::Closure request = base::Bind(
46      &DaemonController::DoGetConfig, this, wrapped_done);
47  ServiceOrQueueRequest(request);
48}
49
50void DaemonController::InstallHost(const CompletionCallback& done) {
51  DCHECK(caller_task_runner_->BelongsToCurrentThread());
52
53  DaemonController::CompletionCallback wrapped_done = base::Bind(
54      &DaemonController::InvokeCompletionCallbackAndScheduleNext, this, done);
55  base::Closure request = base::Bind(
56      &DaemonController::DoInstallHost, this, wrapped_done);
57  ServiceOrQueueRequest(request);
58}
59
60void DaemonController::SetConfigAndStart(
61    scoped_ptr<base::DictionaryValue> config,
62    bool consent,
63    const CompletionCallback& done) {
64  DCHECK(caller_task_runner_->BelongsToCurrentThread());
65
66  DaemonController::CompletionCallback wrapped_done = base::Bind(
67      &DaemonController::InvokeCompletionCallbackAndScheduleNext, this, done);
68  base::Closure request = base::Bind(
69      &DaemonController::DoSetConfigAndStart, this, base::Passed(&config),
70      consent, wrapped_done);
71  ServiceOrQueueRequest(request);
72}
73
74void DaemonController::UpdateConfig(scoped_ptr<base::DictionaryValue> config,
75                                    const CompletionCallback& done) {
76  DCHECK(caller_task_runner_->BelongsToCurrentThread());
77
78  DaemonController::CompletionCallback wrapped_done = base::Bind(
79      &DaemonController::InvokeCompletionCallbackAndScheduleNext, this, done);
80  base::Closure request = base::Bind(
81      &DaemonController::DoUpdateConfig, this, base::Passed(&config),
82      wrapped_done);
83  ServiceOrQueueRequest(request);
84}
85
86void DaemonController::Stop(const CompletionCallback& done) {
87  DCHECK(caller_task_runner_->BelongsToCurrentThread());
88
89  DaemonController::CompletionCallback wrapped_done = base::Bind(
90      &DaemonController::InvokeCompletionCallbackAndScheduleNext, this, done);
91  base::Closure request = base::Bind(
92      &DaemonController::DoStop, this, wrapped_done);
93  ServiceOrQueueRequest(request);
94}
95
96void DaemonController::SetWindow(void* window_handle) {
97  DCHECK(caller_task_runner_->BelongsToCurrentThread());
98
99  base::Closure done = base::Bind(&DaemonController::ScheduleNext, this);
100  base::Closure request = base::Bind(
101      &DaemonController::DoSetWindow, this, window_handle, done);
102  ServiceOrQueueRequest(request);
103}
104
105void DaemonController::GetVersion(const GetVersionCallback& done) {
106  DCHECK(caller_task_runner_->BelongsToCurrentThread());
107
108  DaemonController::GetVersionCallback wrapped_done = base::Bind(
109      &DaemonController::InvokeVersionCallbackAndScheduleNext, this, done);
110  base::Closure request = base::Bind(
111      &DaemonController::DoGetVersion, this, wrapped_done);
112  ServiceOrQueueRequest(request);
113}
114
115void DaemonController::GetUsageStatsConsent(
116    const GetUsageStatsConsentCallback& done) {
117  DCHECK(caller_task_runner_->BelongsToCurrentThread());
118
119  DaemonController::GetUsageStatsConsentCallback wrapped_done = base::Bind(
120      &DaemonController::InvokeConsentCallbackAndScheduleNext, this, done);
121  base::Closure request = base::Bind(
122      &DaemonController::DoGetUsageStatsConsent, this, wrapped_done);
123  ServiceOrQueueRequest(request);
124}
125
126DaemonController::~DaemonController() {
127  // Make sure |delegate_| is deleted on the background thread.
128  delegate_task_runner_->DeleteSoon(FROM_HERE, delegate_.release());
129
130  // Stop the thread.
131  delegate_task_runner_ = NULL;
132  caller_task_runner_->DeleteSoon(FROM_HERE, delegate_thread_.release());
133}
134
135void DaemonController::DoGetConfig(const GetConfigCallback& done) {
136  DCHECK(delegate_task_runner_->BelongsToCurrentThread());
137
138  scoped_ptr<base::DictionaryValue> config = delegate_->GetConfig();
139  caller_task_runner_->PostTask(FROM_HERE,
140                                base::Bind(done, base::Passed(&config)));
141}
142
143void DaemonController::DoInstallHost(const CompletionCallback& done) {
144  DCHECK(delegate_task_runner_->BelongsToCurrentThread());
145
146  delegate_->InstallHost(done);
147}
148
149void DaemonController::DoSetConfigAndStart(
150    scoped_ptr<base::DictionaryValue> config,
151    bool consent,
152    const CompletionCallback& done) {
153  DCHECK(delegate_task_runner_->BelongsToCurrentThread());
154
155  delegate_->SetConfigAndStart(config.Pass(), consent, done);
156}
157
158void DaemonController::DoUpdateConfig(
159    scoped_ptr<base::DictionaryValue> config,
160    const CompletionCallback& done) {
161  DCHECK(delegate_task_runner_->BelongsToCurrentThread());
162
163  delegate_->UpdateConfig(config.Pass(), done);
164}
165
166void DaemonController::DoStop(const CompletionCallback& done) {
167  DCHECK(delegate_task_runner_->BelongsToCurrentThread());
168
169  delegate_->Stop(done);
170}
171
172void DaemonController::DoSetWindow(void* window_handle,
173                                   const base::Closure& done) {
174  DCHECK(delegate_task_runner_->BelongsToCurrentThread());
175
176  delegate_->SetWindow(window_handle);
177  caller_task_runner_->PostTask(FROM_HERE, done);
178}
179
180void DaemonController::DoGetVersion(const GetVersionCallback& done) {
181  DCHECK(delegate_task_runner_->BelongsToCurrentThread());
182
183  std::string version = delegate_->GetVersion();
184  caller_task_runner_->PostTask(FROM_HERE, base::Bind(done, version));
185}
186
187void DaemonController::DoGetUsageStatsConsent(
188    const GetUsageStatsConsentCallback& done) {
189  DCHECK(delegate_task_runner_->BelongsToCurrentThread());
190
191  DaemonController::UsageStatsConsent consent =
192      delegate_->GetUsageStatsConsent();
193  caller_task_runner_->PostTask(FROM_HERE, base::Bind(done, consent));
194}
195
196void DaemonController::InvokeCompletionCallbackAndScheduleNext(
197    const CompletionCallback& done,
198    AsyncResult result) {
199  if (!caller_task_runner_->BelongsToCurrentThread()) {
200    caller_task_runner_->PostTask(
201        FROM_HERE,
202        base::Bind(&DaemonController::InvokeCompletionCallbackAndScheduleNext,
203                   this, done, result));
204    return;
205  }
206
207  done.Run(result);
208  ScheduleNext();
209}
210
211void DaemonController::InvokeConfigCallbackAndScheduleNext(
212    const GetConfigCallback& done,
213    scoped_ptr<base::DictionaryValue> config) {
214  DCHECK(caller_task_runner_->BelongsToCurrentThread());
215
216  done.Run(config.Pass());
217  ScheduleNext();
218}
219
220void DaemonController::InvokeConsentCallbackAndScheduleNext(
221    const GetUsageStatsConsentCallback& done,
222    const UsageStatsConsent& consent) {
223  DCHECK(caller_task_runner_->BelongsToCurrentThread());
224
225  done.Run(consent);
226  ScheduleNext();
227}
228
229void DaemonController::InvokeVersionCallbackAndScheduleNext(
230    const GetVersionCallback& done,
231    const std::string& version) {
232  DCHECK(caller_task_runner_->BelongsToCurrentThread());
233
234  done.Run(version);
235  ScheduleNext();
236}
237
238void DaemonController::ScheduleNext() {
239  DCHECK(caller_task_runner_->BelongsToCurrentThread());
240
241  pending_requests_.pop();
242  ServiceNextRequest();
243}
244
245void DaemonController::ServiceOrQueueRequest(const base::Closure& request) {
246  bool servicing_request = !pending_requests_.empty();
247  pending_requests_.push(request);
248  if (!servicing_request)
249    ServiceNextRequest();
250}
251
252void DaemonController::ServiceNextRequest() {
253  if (!pending_requests_.empty())
254    delegate_task_runner_->PostTask(FROM_HERE, pending_requests_.front());
255}
256
257}  // namespace remoting
258