Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(553)

Side by Side Diff: components/arc/arc_bridge_service.cc

Issue 1412863004: arc-bridge: Add the ARC Bridge Service (Closed) Base URL: https://chromium.googlesource.com/a/chromium/src.git@master
Patch Set: Improved thread-safety Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include "components/arc/arc_bridge_service.h"
6
7 #include "base/files/file_path.h"
8 #include "base/files/file_util.h"
9 #include "base/prefs/pref_registry_simple.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/task_runner_util.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "chromeos/arc/bridge/common/arc_host_messages.h"
14 #include "chromeos/arc/bridge/common/arc_instance_messages.h"
15 #include "chromeos/dbus/arc_instance_client.h"
16 #include "chromeos/dbus/dbus_method_call_status.h"
17 #include "chromeos/dbus/dbus_thread_manager.h"
18 #include "components/arc/arc_pref_names.h"
19 #include "ipc/ipc_channel.h"
20
21 namespace {
22
23 const base::FilePath::CharType kArcBridgeSocketPath[] =
24 FILE_PATH_LITERAL("/home/chronos/ArcBridge/bridge.sock");
25
26 } // namespace
27
28 namespace arc {
29
30 ArcBridgeService::ArcBridgeService(
31 const scoped_refptr<base::SequencedTaskRunner>& file_task_runner)
Shuhei Takahashi 2015/11/02 17:58:02 nit: excess indent (4 spaces here) In general I r
Luis Héctor Chávez 2015/11/04 17:49:18 I messed up with my Vim setup recently and lost th
Luis Héctor Chávez 2015/11/09 16:41:04 Turns out this was the correct indentation :/
32 : ipc_thread_("ARC bridge listener"),
33 origin_task_runner_(base::ThreadTaskRunnerHandle::Get()),
34 file_task_runner_(file_task_runner),
35 observer_list_(new base::ObserverListThreadSafe<Observer>()),
36 available_(false),
37 enabled_(false),
38 state_(ArcBridgeService::STOPPED),
39 weak_factory_(this) {
40 ipc_thread_.StartWithOptions(
41 base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
42 }
43
44 ArcBridgeService::~ArcBridgeService() {}
45
46 // static
47 void ArcBridgeService::RegisterPrefs(PrefRegistrySimple* registry) {
48 registry->RegisterBooleanPref(prefs::kArcEnabled, false);
49 }
50
51 // static
52 bool ArcBridgeService::GetEnabledPref(PrefService* pref_service) {
53 // TODO(lhchavez): Once this is user-configurable, use the real pref.
54 return true;
55 }
56
57 void ArcBridgeService::HandleStartup() {
58 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread());
59 if (!enabled_)
60 return;
61 SocketConnect(base::FilePath(kArcBridgeSocketPath));
62 }
63
64 void ArcBridgeService::Shutdown() {
65 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread());
66 if (state_ == ArcBridgeService::STOPPED ||
67 state_ == ArcBridgeService::STOPPING) {
68 LOG(ERROR) << "Shutdown() called when ARC is not running";
oshima 2015/11/02 19:13:49 This file is using too many LOG(ERROR). Consider u
Luis Héctor Chávez 2015/11/04 17:49:18 Done.
69 return;
70 }
71 if (state_ == ArcBridgeService::CONNECTED ||
72 state_ == ArcBridgeService::CONNECTING) {
73 // This was stopped before the D-Bus command to start the instance. Just
74 // close the socket (if it was opened).
75 if (state_ == ArcBridgeService::CONNECTED) {
76 base::AutoLock scoped_lock(ipc_channel_lock_);
77 ipc_channel_->Close();
78 ipc_channel_.reset();
79 }
80 SetState(ArcBridgeService::STOPPED);
81 return;
82 }
83
84 SetState(ArcBridgeService::STOPPING);
85 chromeos::DBusThreadManager::Get()->GetArcInstanceClient()->StopInstance(
86 base::Bind(&ArcBridgeService::OnInstanceStopped,
87 weak_factory_.GetWeakPtr()));
Shuhei Takahashi 2015/11/02 17:58:02 nit: wrong indent
Luis Héctor Chávez 2015/11/04 17:49:18 Done.
88 }
89
90 void ArcBridgeService::AddObserver(Observer* observer) {
91 observer_list_->AddObserver(observer);
92 }
93
94 void ArcBridgeService::RemoveObserver(Observer* observer) {
95 observer_list_->RemoveObserver(observer);
96 }
97
98 void ArcBridgeService::SetEnabled(bool enabled) {
99 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread());
100
101 if (enabled_ == enabled)
102 return;
103 enabled_ = enabled;
104 if (!enabled_ && state_ != ArcBridgeService::STOPPED &&
105 state_ != ArcBridgeService::STOPPING)
106 Shutdown();
107 observer_list_->Notify(FROM_HERE,
108 &Observer::OnEnabledChanged,
109 enabled_);
110 }
111
112 bool ArcBridgeService::RegisterInputDevice(
113 const std::string& name, const std::string& device_type,
114 base::ScopedFD fd) {
115 // ipc_channel_->Send() is thread-safe, so there is no thread check.
Shuhei Takahashi 2015/11/02 17:58:02 This comment is obsolete.
Luis Héctor Chávez 2015/11/04 17:49:18 Done.
116 {
117 base::AutoLock scoped_lock(ipc_channel_lock_);
118 if (!ipc_channel_) {
119 LOG(ERROR) << "Called RegisterInputDevice when the service is not ready";
120 return false;
121 }
122 return ipc_channel_->Send(new ArcInstanceMsg_RegisterInputDevice(
123 name, device_type, base::FileDescriptor(fd.Pass())));
124 }
125 }
126
127 void ArcBridgeService::SocketConnect(const base::FilePath& socket_path) {
128 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread());
129 if (state_ != ArcBridgeService::STOPPED) {
130 LOG(ERROR) << "SocketConnect() called when instance is not stopped";
131 return;
132 }
133 SetState(ArcBridgeService::CONNECTING);
134 base::PostTaskAndReplyWithResult(
135 file_task_runner_.get(),
136 FROM_HERE,
137 base::Bind(&base::CreateDirectory, socket_path.DirName()),
138 base::Bind(&ArcBridgeService::SocketConnectAfterEnsureParentDirectory,
139 weak_factory_.GetWeakPtr(),
140 socket_path));
141 }
142
143 void ArcBridgeService::SocketConnectAfterEnsureParentDirectory(
144 const base::FilePath& socket_path, bool directory_present) {
145 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread());
146 if (state_ != ArcBridgeService::CONNECTING) {
147 LOG(ERROR) << "Shutdown requested while connecting";
148 return;
149 }
150 if (!directory_present) {
151 LOG(ERROR) << "Error creating directory for " << socket_path.value();
152 Shutdown();
153 return;
154 }
155
156 if (!Connect(IPC::ChannelHandle(socket_path.value()),
157 IPC::Channel::MODE_OPEN_NAMED_SERVER)) {
158 LOG(ERROR) << "Error connecting to " << socket_path.value();
159 Shutdown();
160 return;
161 }
162
163 base::PostTaskAndReplyWithResult(
164 file_task_runner_.get(),
165 FROM_HERE,
166 // TODO(lhchavez): Tighten the security around the socket by tying it to
167 // the user the instance will run as.
168 base::Bind(&base::SetPosixFilePermissions, socket_path, 0777),
169 base::Bind(&ArcBridgeService::SocketConnectAfterSetSocketPermissions,
170 weak_factory_.GetWeakPtr(), socket_path));
171 }
172
173 void ArcBridgeService::SocketConnectAfterSetSocketPermissions(
174 const base::FilePath& socket_path, bool socket_permissions_success) {
175 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread());
176 if (state_ != ArcBridgeService::CONNECTED) {
177 LOG(ERROR) << "Shutdown requested while connecting";
178 return;
179 }
180
181 if (!socket_permissions_success) {
182 LOG(ERROR) << "Error setting socket permissions for "
183 << socket_path.value();
184 Shutdown();
185 return;
186 }
187
188 SetState(ArcBridgeService::STARTING);
189 // This will fail if the ArcInstanceService is not running on Chrome OS.
190 chromeos::DBusThreadManager::Get()->GetArcInstanceClient()->StartInstance(
191 socket_path.value(),
192 base::Bind(&ArcBridgeService::OnInstanceStarted,
193 weak_factory_.GetWeakPtr()));
194 }
195
196 bool ArcBridgeService::Connect(const IPC::ChannelHandle& handle,
197 IPC::Channel::Mode mode) {
198 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread());
199 // Testing code can have the state in STOPPED since it does not go through
200 // the whole connection flow.
201 if (state_ != ArcBridgeService::CONNECTING &&
202 state_ != ArcBridgeService::STOPPED) {
203 LOG(ERROR) << "Shutdown requested while connecting";
204 return false;
205 }
206
207 base::AutoLock scoped_lock(ipc_channel_lock_);
208 ipc_channel_ = IPC::ChannelProxy::Create(handle, mode, this,
209 ipc_thread_.task_runner().get());
210 if (!ipc_channel_)
211 return false;
212 SetState(ArcBridgeService::CONNECTED);
213 return true;
214 }
215
216 void ArcBridgeService::OnInstanceReady() {
217 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread());
218 if (state_ != ArcBridgeService::STARTING) {
219 LOG(ERROR) << "Shutdown requested while connecting";
220 return;
221 }
222 SetState(ArcBridgeService::READY);
223 }
224
225 void ArcBridgeService::SetState(State state) {
226 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread());
227 if (state_ == state)
Shuhei Takahashi 2015/11/02 17:58:02 I think we should rather ensure this condition: D
Luis Héctor Chávez 2015/11/04 17:49:18 Done.
228 return;
229 state_ = state;
230 observer_list_->Notify(FROM_HERE,
231 &Observer::OnStateChanged,
232 state_);
233 }
234
235 bool ArcBridgeService::OnMessageReceived(const IPC::Message& message) {
236 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread());
237 bool handled = true;
238
239 IPC_BEGIN_MESSAGE_MAP(ArcBridgeService, message)
240 IPC_MESSAGE_HANDLER(ArcInstanceHostMsg_InstanceReady, OnInstanceReady)
241 IPC_MESSAGE_UNHANDLED(handled = false)
242 IPC_END_MESSAGE_MAP()
243
244 if (!handled)
245 LOG(ERROR) << "Invalid message with type = " << message.type();
246 return handled;
247 }
248
249 void ArcBridgeService::OnInstanceStarted(
250 chromeos::DBusMethodCallStatus status) {
251 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread());
252 if (state_ != ArcBridgeService::STARTING) {
253 LOG(ERROR) << "Shutdown requested while connecting";
254 return;
255 }
256 if (status != chromeos::DBUS_METHOD_CALL_SUCCESS) {
257 LOG(ERROR) << "ARC instance unable to start. Shutting down the bridge";
258 Shutdown();
259 return;
260 }
261 }
262
263 void ArcBridgeService::OnInstanceStopped(
264 chromeos::DBusMethodCallStatus status) {
265 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread());
266 // STOPPING is the only valid state for this function.
267 CHECK(state_ == ArcBridgeService::STOPPING);
Shuhei Takahashi 2015/11/02 17:58:02 Maybe DCHECK? I remember Chrome should avoid CHECK
Luis Héctor Chávez 2015/11/04 17:49:18 Done.
268 {
269 base::AutoLock scoped_lock(ipc_channel_lock_);
270 ipc_channel_->Close();
271 ipc_channel_.reset();
272 }
273 SetState(ArcBridgeService::STOPPED);
274 }
275
276 } // namespace arc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698