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::SetConfigAndStart(
51    scoped_ptr<base::DictionaryValue> config,
52    bool consent,
53    const CompletionCallback& done) {
54  DCHECK(caller_task_runner_->BelongsToCurrentThread());
55
56  DaemonController::CompletionCallback wrapped_done = base::Bind(
57      &DaemonController::InvokeCompletionCallbackAndScheduleNext, this, done);
58  base::Closure request = base::Bind(
59      &DaemonController::DoSetConfigAndStart, this, base::Passed(&config),
60      consent, wrapped_done);
61  ServiceOrQueueRequest(request);
62}
63
64void DaemonController::UpdateConfig(scoped_ptr<base::DictionaryValue> config,
65                                    const CompletionCallback& done) {
66  DCHECK(caller_task_runner_->BelongsToCurrentThread());
67
68  DaemonController::CompletionCallback wrapped_done = base::Bind(
69      &DaemonController::InvokeCompletionCallbackAndScheduleNext, this, done);
70  base::Closure request = base::Bind(
71      &DaemonController::DoUpdateConfig, this, base::Passed(&config),
72      wrapped_done);
73  ServiceOrQueueRequest(request);
74}
75
76void DaemonController::Stop(const CompletionCallback& done) {
77  DCHECK(caller_task_runner_->BelongsToCurrentThread());
78
79  DaemonController::CompletionCallback wrapped_done = base::Bind(
80      &DaemonController::InvokeCompletionCallbackAndScheduleNext, this, done);
81  base::Closure request = base::Bind(
82      &DaemonController::DoStop, this, wrapped_done);
83  ServiceOrQueueRequest(request);
84}
85
86void DaemonController::SetWindow(void* window_handle) {
87  DCHECK(caller_task_runner_->BelongsToCurrentThread());
88
89  base::Closure done = base::Bind(&DaemonController::ScheduleNext, this);
90  base::Closure request = base::Bind(
91      &DaemonController::DoSetWindow, this, window_handle, done);
92  ServiceOrQueueRequest(request);
93}
94
95void DaemonController::GetVersion(const GetVersionCallback& done) {
96  DCHECK(caller_task_runner_->BelongsToCurrentThread());
97
98  DaemonController::GetVersionCallback wrapped_done = base::Bind(
99      &DaemonController::InvokeVersionCallbackAndScheduleNext, this, done);
100  base::Closure request = base::Bind(
101      &DaemonController::DoGetVersion, this, wrapped_done);
102  ServiceOrQueueRequest(request);
103}
104
105void DaemonController::GetUsageStatsConsent(
106    const GetUsageStatsConsentCallback& done) {
107  DCHECK(caller_task_runner_->BelongsToCurrentThread());
108
109  DaemonController::GetUsageStatsConsentCallback wrapped_done = base::Bind(
110      &DaemonController::InvokeConsentCallbackAndScheduleNext, this, done);
111  base::Closure request = base::Bind(
112      &DaemonController::DoGetUsageStatsConsent, this, wrapped_done);
113  ServiceOrQueueRequest(request);
114}
115
116DaemonController::~DaemonController() {
117  // Make sure |delegate_| is deleted on the background thread.
118  delegate_task_runner_->DeleteSoon(FROM_HERE, delegate_.release());
119
120  // Stop the thread.
121  delegate_task_runner_ = NULL;
122  caller_task_runner_->DeleteSoon(FROM_HERE, delegate_thread_.release());
123}
124
125void DaemonController::DoGetConfig(const GetConfigCallback& done) {
126  DCHECK(delegate_task_runner_->BelongsToCurrentThread());
127
128  scoped_ptr<base::DictionaryValue> config = delegate_->GetConfig();
129  caller_task_runner_->PostTask(FROM_HERE,
130                                base::Bind(done, base::Passed(&config)));
131}
132
133void DaemonController::DoSetConfigAndStart(
134    scoped_ptr<base::DictionaryValue> config,
135    bool consent,
136    const CompletionCallback& done) {
137  DCHECK(delegate_task_runner_->BelongsToCurrentThread());
138
139  delegate_->SetConfigAndStart(config.Pass(), consent, done);
140}
141
142void DaemonController::DoUpdateConfig(
143    scoped_ptr<base::DictionaryValue> config,
144    const CompletionCallback& done) {
145  DCHECK(delegate_task_runner_->BelongsToCurrentThread());
146
147  delegate_->UpdateConfig(config.Pass(), done);
148}
149
150void DaemonController::DoStop(const CompletionCallback& done) {
151  DCHECK(delegate_task_runner_->BelongsToCurrentThread());
152
153  delegate_->Stop(done);
154}
155
156void DaemonController::DoSetWindow(void* window_handle,
157                                   const base::Closure& done) {
158  DCHECK(delegate_task_runner_->BelongsToCurrentThread());
159
160  delegate_->SetWindow(window_handle);
161  caller_task_runner_->PostTask(FROM_HERE, done);
162}
163
164void DaemonController::DoGetVersion(const GetVersionCallback& done) {
165  DCHECK(delegate_task_runner_->BelongsToCurrentThread());
166
167  std::string version = delegate_->GetVersion();
168  caller_task_runner_->PostTask(FROM_HERE, base::Bind(done, version));
169}
170
171void DaemonController::DoGetUsageStatsConsent(
172    const GetUsageStatsConsentCallback& done) {
173  DCHECK(delegate_task_runner_->BelongsToCurrentThread());
174
175  DaemonController::UsageStatsConsent consent =
176      delegate_->GetUsageStatsConsent();
177  caller_task_runner_->PostTask(FROM_HERE, base::Bind(done, consent));
178}
179
180void DaemonController::InvokeCompletionCallbackAndScheduleNext(
181    const CompletionCallback& done,
182    AsyncResult result) {
183  if (!caller_task_runner_->BelongsToCurrentThread()) {
184    caller_task_runner_->PostTask(
185        FROM_HERE,
186        base::Bind(&DaemonController::InvokeCompletionCallbackAndScheduleNext,
187                   this, done, result));
188    return;
189  }
190
191  done.Run(result);
192  ScheduleNext();
193}
194
195void DaemonController::InvokeConfigCallbackAndScheduleNext(
196    const GetConfigCallback& done,
197    scoped_ptr<base::DictionaryValue> config) {
198  DCHECK(caller_task_runner_->BelongsToCurrentThread());
199
200  done.Run(config.Pass());
201  ScheduleNext();
202}
203
204void DaemonController::InvokeConsentCallbackAndScheduleNext(
205    const GetUsageStatsConsentCallback& done,
206    const UsageStatsConsent& consent) {
207  DCHECK(caller_task_runner_->BelongsToCurrentThread());
208
209  done.Run(consent);
210  ScheduleNext();
211}
212
213void DaemonController::InvokeVersionCallbackAndScheduleNext(
214    const GetVersionCallback& done,
215    const std::string& version) {
216  DCHECK(caller_task_runner_->BelongsToCurrentThread());
217
218  done.Run(version);
219  ScheduleNext();
220}
221
222void DaemonController::ScheduleNext() {
223  DCHECK(caller_task_runner_->BelongsToCurrentThread());
224
225  pending_requests_.pop();
226  ServiceNextRequest();
227}
228
229void DaemonController::ServiceOrQueueRequest(const base::Closure& request) {
230  bool servicing_request = !pending_requests_.empty();
231  pending_requests_.push(request);
232  if (!servicing_request)
233    ServiceNextRequest();
234}
235
236void DaemonController::ServiceNextRequest() {
237  if (!pending_requests_.empty())
238    delegate_task_runner_->PostTask(FROM_HERE, pending_requests_.front());
239}
240
241}  // namespace remoting
242