OLD | NEW |
---|---|
(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 // Ensures the parent directory of |socket_path| is present. Returns true if it | |
27 // was created or if it was already present. | |
28 bool EnsureParentDirectoryPresent(const base::FilePath& socket_path) { | |
29 base::FilePath path(socket_path); | |
30 return base::CreateDirectory(path.DirName()); | |
31 } | |
32 | |
33 // Changes the permissions on the socket so that the ARC instance can use it. | |
34 bool SetSocketPermissions(const base::FilePath& socket_path) { | |
35 // TODO(lhchavez): Tighten the security around the socket by tying it to the | |
Shuhei Takahashi
2015/10/28 21:13:57
Could you file a bug so we don't forget this?
Luis Héctor Chávez
2015/10/28 23:12:20
It's part of the same bug.
| |
36 // user the instance will run as. | |
37 return HANDLE_EINTR(chmod(socket_path.value().c_str(), 0777)) == 0; | |
38 } | |
39 | |
40 } // namespace | |
41 | |
42 namespace arc { | |
43 | |
44 ArcBridgeService::ArcBridgeService( | |
45 const scoped_refptr<base::SequencedTaskRunner>& file_task_runner) | |
46 : ipc_thread_("ARC bridge listener"), | |
47 origin_task_runner_(base::ThreadTaskRunnerHandle::Get()), | |
48 file_task_runner_(file_task_runner), | |
49 available_(false), | |
Shuhei Takahashi
2015/10/28 21:13:57
Hmm,I don't see any code setting available_ to tru
Luis Héctor Chávez
2015/10/28 23:12:20
It's not there yet, but I want to have it availabl
| |
50 enabled_(false), | |
51 state_(ArcBridgeService::STOPPED), | |
52 weak_factory_(this) { | |
53 ipc_thread_.StartWithOptions( | |
54 base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); | |
55 } | |
56 | |
57 ArcBridgeService::~ArcBridgeService() {} | |
58 | |
59 // static | |
60 void ArcBridgeService::RegisterPrefs(PrefRegistrySimple* registry) { | |
61 registry->RegisterBooleanPref(prefs::kArcEnabled, false); | |
62 } | |
63 | |
64 // static | |
65 bool ArcBridgeService::GetEnabledPref(PrefService* pref_service) { | |
66 // TODO(lhchavez): Once this is user-configurable, use the real pref. | |
67 return true; | |
68 } | |
69 | |
70 void ArcBridgeService::HandleStartup() { | |
71 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | |
72 if (!enabled_ || state_ != ArcBridgeService::STOPPED) | |
73 return; | |
74 base::PostTaskAndReplyWithResult( | |
75 file_task_runner_.get(), | |
76 FROM_HERE, | |
77 base::Bind(&EnsureParentDirectoryPresent, | |
78 base::FilePath(kArcBridgeSocketPath)), | |
79 base::Bind(&ArcBridgeService::SocketConnect, | |
80 weak_factory_.GetWeakPtr(), | |
81 base::FilePath(kArcBridgeSocketPath))); | |
82 } | |
83 | |
84 void ArcBridgeService::Shutdown() { | |
85 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | |
86 if (state_ != ArcBridgeService::CONNECTED && | |
87 state_ != ArcBridgeService::STARTING && | |
88 state_ != ArcBridgeService::READY) | |
89 return; | |
90 ipc_channel_->Close(); | |
Shuhei Takahashi
2015/10/28 21:13:57
Could you set ipc_channel_ to null?
Luis Héctor Chávez
2015/10/28 23:12:20
Done.
| |
91 base::PostTaskAndReplyWithResult( | |
92 file_task_runner_.get(), | |
93 FROM_HERE, | |
94 base::Bind(&base::DeleteFile, base::FilePath(kArcBridgeSocketPath), | |
95 false), | |
96 base::Bind(&ArcBridgeService::FinishShutdownAfterSocketDeleted, | |
97 weak_factory_.GetWeakPtr())); | |
98 } | |
99 | |
100 void ArcBridgeService::AddObserver(Observer* observer) { | |
101 observer_list_.AddObserver(observer); | |
Shuhei Takahashi
2015/10/28 21:13:57
ObserverList is not thread-safe. Could you insert
Luis Héctor Chávez
2015/10/28 23:12:20
Done.
| |
102 } | |
103 | |
104 void ArcBridgeService::RemoveObserver(Observer* observer) { | |
105 observer_list_.RemoveObserver(observer); | |
106 } | |
107 | |
108 void ArcBridgeService::SetEnabled(bool enabled) { | |
109 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | |
110 | |
111 if (enabled_ == enabled) | |
112 return; | |
113 enabled_ = enabled; | |
114 if (!enabled_) | |
115 Shutdown(); | |
Shuhei Takahashi
2015/10/28 21:13:57
Shutdown() will fail when state_ is either STOPPED
Luis Héctor Chávez
2015/10/28 23:12:20
Good catch. Done.
The lifetime of ArcBridgeServic
| |
116 FOR_EACH_OBSERVER(Observer, observer_list_, OnEnabledChanged(enabled_)); | |
117 } | |
118 | |
119 bool ArcBridgeService::RegisterInputDevice( | |
120 const std::string& name, const std::string& device_type, | |
121 base::ScopedFD fd) { | |
122 return ipc_channel_->Send(new ArcInstanceMsg_RegisterInputDevice( | |
Shuhei Takahashi
2015/10/28 21:13:57
Could you check state_? In some cases ipc_channel_
Luis Héctor Chávez
2015/10/28 23:12:20
Done.
| |
123 name, device_type, base::FileDescriptor(fd.Pass()))); | |
124 } | |
125 | |
126 void ArcBridgeService::SocketConnect(const base::FilePath& socket_path, | |
127 bool directory_creation_success) { | |
128 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | |
129 | |
130 if (!directory_creation_success) { | |
131 LOG(ERROR) << "Error creating directory for " << socket_path.value(); | |
132 NotifyStateChanged(ArcBridgeService::ERROR); | |
133 return; | |
134 } | |
135 | |
136 if (!Connect(IPC::ChannelHandle(socket_path.value()), | |
137 IPC::Channel::MODE_OPEN_NAMED_SERVER)) { | |
138 LOG(ERROR) << "Error connecting to " << socket_path.value(); | |
139 NotifyStateChanged(ArcBridgeService::ERROR); | |
140 return; | |
141 } | |
142 | |
143 base::PostTaskAndReplyWithResult( | |
144 file_task_runner_.get(), | |
145 FROM_HERE, | |
146 base::Bind(&SetSocketPermissions, socket_path), | |
147 base::Bind(&ArcBridgeService::FinishConnectAfterSetSocketPermissions, | |
148 weak_factory_.GetWeakPtr(), socket_path)); | |
149 } | |
150 | |
151 void ArcBridgeService::FinishConnectAfterSetSocketPermissions( | |
152 const base::FilePath& socket_path, bool socket_permissions_success) { | |
153 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | |
154 | |
155 if (!socket_permissions_success) { | |
156 LOG(ERROR) << "Error setting socket permissions for " | |
157 << socket_path.value(); | |
158 NotifyStateChanged(ArcBridgeService::ERROR); | |
159 return; | |
160 } | |
161 | |
162 // This will fail if the ArcInstanceService is not running on Chrome OS. | |
163 // Use base::Unretained since this is a KeyedService. | |
Shuhei Takahashi
2015/10/28 21:13:57
Unretained seems not used here?
Luis Héctor Chávez
2015/10/28 23:12:20
Missed that one. Done.
| |
164 chromeos::DBusThreadManager::Get()->GetArcInstanceClient()->StartInstance( | |
165 socket_path.value(), | |
166 base::Bind(&ArcBridgeService::OnInstanceStarted, | |
167 weak_factory_.GetWeakPtr())); | |
168 } | |
169 | |
170 | |
171 bool ArcBridgeService::Connect(const IPC::ChannelHandle& handle, | |
172 IPC::Channel::Mode mode) { | |
173 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | |
174 | |
175 ipc_channel_ = IPC::ChannelProxy::Create(handle, mode, this, | |
176 ipc_thread_.task_runner().get()); | |
177 if (!ipc_channel_) | |
178 return false; | |
179 NotifyStateChanged(ArcBridgeService::CONNECTED); | |
Shuhei Takahashi
2015/10/28 21:13:57
It is confusing that state is changed only when IP
Luis Héctor Chávez
2015/10/28 23:12:20
Connect is also used from tests, so it needs to st
| |
180 return true; | |
181 } | |
182 | |
183 void ArcBridgeService::OnInstanceReady() { | |
184 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | |
185 NotifyStateChanged(ArcBridgeService::READY); | |
186 } | |
187 | |
188 void ArcBridgeService::NotifyStateChanged(State state) { | |
Shuhei Takahashi
2015/10/28 21:13:57
This function does set the current state, not only
Luis Héctor Chávez
2015/10/28 23:12:20
Done.
| |
189 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | |
190 if (state_ == state) | |
191 return; | |
192 state_ = state; | |
193 FOR_EACH_OBSERVER(Observer, observer_list_, OnStateChanged(state_)); | |
194 } | |
195 | |
196 void ArcBridgeService::FinishShutdownAfterSocketDeleted(bool socket_deleted) { | |
197 if (state_ != ArcBridgeService::STARTING && state_ != ArcBridgeService::READY) | |
198 return; | |
Shuhei Takahashi
2015/10/28 21:13:57
In this code path, state_ is not reset to STOPPED.
Luis Héctor Chávez
2015/10/28 23:12:20
It still needs to wait for the DBus message to ret
| |
199 chromeos::DBusThreadManager::Get()->GetArcInstanceClient()->StopInstance( | |
200 base::Bind(&ArcBridgeService::OnInstanceStopped, | |
201 weak_factory_.GetWeakPtr())); | |
202 } | |
203 | |
204 bool ArcBridgeService::OnMessageReceived(const IPC::Message& message) { | |
205 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | |
206 bool handled = true; | |
207 | |
208 IPC_BEGIN_MESSAGE_MAP(ArcBridgeService, message) | |
209 IPC_MESSAGE_HANDLER(ArcInstanceHostMsg_InstanceReady, OnInstanceReady) | |
210 IPC_MESSAGE_UNHANDLED(handled = false) | |
211 IPC_END_MESSAGE_MAP() | |
212 | |
213 if (!handled) | |
214 LOG(ERROR) << "Invalid message with type = " << message.type(); | |
215 return handled; | |
216 } | |
217 | |
218 void ArcBridgeService::OnInstanceStarted( | |
Shuhei Takahashi
2015/10/28 21:13:57
Could you swap the position of OnInstanceStarted()
Luis Héctor Chávez
2015/10/28 23:12:20
I don't want to reorder any further until it's rea
| |
219 chromeos::DBusMethodCallStatus status) { | |
220 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | |
221 if (status != chromeos::DBUS_METHOD_CALL_SUCCESS) { | |
222 LOG(ERROR) << "ARC instance unable to start. Shutting down the bridge"; | |
223 Shutdown(); | |
224 return; | |
225 } | |
226 NotifyStateChanged(ArcBridgeService::STARTING); | |
227 } | |
228 | |
229 void ArcBridgeService::OnInstanceStopped( | |
230 chromeos::DBusMethodCallStatus status) { | |
231 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | |
232 NotifyStateChanged(ArcBridgeService::STOPPED); | |
233 } | |
234 | |
235 } // namespace arc | |
OLD | NEW |