1 // Copyright 2019 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 "chromecast/external_mojo/public/cpp/external_mojo_broker.h"
6
7 #include <map>
8 #include <utility>
9
10 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_BSD)
11 #include <sys/stat.h>
12 #endif
13
14 #include "base/bind.h"
15 #include "base/callback_helpers.h"
16 #include "base/location.h"
17 #include "base/logging.h"
18 #include "base/macros.h"
19 #include "base/message_loop/message_pump_for_io.h"
20 #include "base/optional.h"
21 #include "base/task/current_thread.h"
22 #include "base/token.h"
23 #include "base/trace_event/trace_event.h"
24 #include "chromecast/external_mojo/public/cpp/common.h"
25 #include "chromecast/external_mojo/public/mojom/connector.mojom.h"
26 #include "mojo/public/cpp/bindings/pending_receiver.h"
27 #include "mojo/public/cpp/bindings/receiver_set.h"
28 #include "mojo/public/cpp/bindings/remote.h"
29 #include "mojo/public/cpp/platform/named_platform_channel.h"
30 #include "mojo/public/cpp/platform/platform_channel_endpoint.h"
31 #include "mojo/public/cpp/platform/platform_handle.h"
32 #include "mojo/public/cpp/platform/socket_utils_posix.h"
33 #include "mojo/public/cpp/system/invitation.h"
34 #include "services/service_manager/public/cpp/connector.h"
35 #include "services/service_manager/public/cpp/constants.h"
36 #include "services/service_manager/public/cpp/identity.h"
37 #include "services/service_manager/public/cpp/service.h"
38 #include "services/service_manager/public/cpp/service_filter.h"
39 #include "services/service_manager/public/cpp/service_receiver.h"
40 #include "services/service_manager/public/mojom/connector.mojom.h"
41 #include "services/service_manager/public/mojom/service.mojom.h"
42
43 namespace chromecast {
44 namespace external_mojo {
45
46 namespace {
47
OnRegisterServiceResult(const std::string & service_name,service_manager::mojom::ConnectResult result)48 void OnRegisterServiceResult(const std::string& service_name,
49 service_manager::mojom::ConnectResult result) {
50 // RegisterServiceInstance() currently returns INVALID_ARGUMENT on success.
51 if (result == service_manager::mojom::ConnectResult::ACCESS_DENIED) {
52 LOG(WARNING) << "Failed to register external service proxy for "
53 << service_name;
54 }
55 }
56
OnInternalBindResult(const std::string & service_name,const std::string & interface_name,service_manager::mojom::ConnectResult result,const base::Optional<service_manager::Identity> & identity)57 void OnInternalBindResult(
58 const std::string& service_name,
59 const std::string& interface_name,
60 service_manager::mojom::ConnectResult result,
61 const base::Optional<service_manager::Identity>& identity) {
62 if (result != service_manager::mojom::ConnectResult::SUCCEEDED) {
63 LOG(ERROR) << "Failed to bind " << service_name << ":" << interface_name
64 << ", result = " << result;
65 }
66 }
67
68 } // namespace
69
70 class ExternalMojoBroker::ConnectorImpl : public mojom::ExternalConnector {
71 public:
ConnectorImpl()72 ConnectorImpl() : connector_facade_(this) {}
73
InitializeChromium(std::unique_ptr<service_manager::Connector> connector,const std::vector<std::string> & external_services_to_proxy)74 void InitializeChromium(
75 std::unique_ptr<service_manager::Connector> connector,
76 const std::vector<std::string>& external_services_to_proxy) {
77 DCHECK(connector);
78 connector_ = std::move(connector);
79 RegisterExternalServices(external_services_to_proxy);
80 }
81
AddReceiver(mojo::PendingReceiver<mojom::ExternalConnector> receiver)82 void AddReceiver(mojo::PendingReceiver<mojom::ExternalConnector> receiver) {
83 receivers_.Add(this, std::move(receiver));
84 }
85
86 private:
87 class ExternalServiceProxy : public ::service_manager::Service {
88 public:
ExternalServiceProxy(ConnectorImpl * connector,std::string service_name,mojo::PendingReceiver<::service_manager::mojom::Service> receiver)89 ExternalServiceProxy(
90 ConnectorImpl* connector,
91 std::string service_name,
92 mojo::PendingReceiver<::service_manager::mojom::Service> receiver)
93 : connector_(connector),
94 service_name_(std::move(service_name)),
95 service_receiver_(this, std::move(receiver)) {
96 DCHECK(connector_);
97 }
98
99 private:
OnBindInterface(const service_manager::BindSourceInfo & source,const std::string & interface_name,mojo::ScopedMessagePipeHandle interface_pipe)100 void OnBindInterface(
101 const service_manager::BindSourceInfo& source,
102 const std::string& interface_name,
103 mojo::ScopedMessagePipeHandle interface_pipe) override {
104 connector_->BindExternalInterface(service_name_, interface_name,
105 std::move(interface_pipe));
106 }
107
108 ConnectorImpl* const connector_;
109 const std::string service_name_;
110 service_manager::ServiceReceiver service_receiver_;
111
112 DISALLOW_COPY_AND_ASSIGN(ExternalServiceProxy);
113 };
114
115 class ServiceManagerConnectorFacade
116 : public service_manager::mojom::Connector {
117 public:
ServiceManagerConnectorFacade(ExternalMojoBroker::ConnectorImpl * connector)118 explicit ServiceManagerConnectorFacade(
119 ExternalMojoBroker::ConnectorImpl* connector)
120 : connector_(connector) {
121 DCHECK(connector_);
122 }
123
AddReceiver(mojo::PendingReceiver<service_manager::mojom::Connector> receiver)124 void AddReceiver(
125 mojo::PendingReceiver<service_manager::mojom::Connector> receiver) {
126 receivers_.Add(this, std::move(receiver));
127 }
128
129 private:
BindInterface(const::service_manager::ServiceFilter & filter,const std::string & interface_name,mojo::ScopedMessagePipeHandle interface_pipe,service_manager::mojom::BindInterfacePriority priority,BindInterfaceCallback callback)130 void BindInterface(const ::service_manager::ServiceFilter& filter,
131 const std::string& interface_name,
132 mojo::ScopedMessagePipeHandle interface_pipe,
133 service_manager::mojom::BindInterfacePriority priority,
134 BindInterfaceCallback callback) override {
135 connector_->BindInterface(filter.service_name(), interface_name,
136 std::move(interface_pipe));
137 std::move(callback).Run(service_manager::mojom::ConnectResult::SUCCEEDED,
138 base::nullopt);
139 }
140
QueryService(const std::string & service_name,QueryServiceCallback callback)141 void QueryService(const std::string& service_name,
142 QueryServiceCallback callback) override {
143 // TODO(kmackay) Could add a wrapper as needed.
144 NOTIMPLEMENTED();
145 }
146
WarmService(const::service_manager::ServiceFilter & filter,WarmServiceCallback callback)147 void WarmService(const ::service_manager::ServiceFilter& filter,
148 WarmServiceCallback callback) override {
149 std::move(callback).Run(service_manager::mojom::ConnectResult::SUCCEEDED,
150 base::nullopt);
151 }
152
RegisterServiceInstance(const::service_manager::Identity & identity,mojo::ScopedMessagePipeHandle service,mojo::PendingReceiver<service_manager::mojom::ProcessMetadata> metadata_receiver,RegisterServiceInstanceCallback callback)153 void RegisterServiceInstance(
154 const ::service_manager::Identity& identity,
155 mojo::ScopedMessagePipeHandle service,
156 mojo::PendingReceiver<service_manager::mojom::ProcessMetadata>
157 metadata_receiver,
158 RegisterServiceInstanceCallback callback) override {
159 // TODO(kmackay) Could add a wrapper as needed.
160 NOTIMPLEMENTED();
161 }
162
Clone(mojo::PendingReceiver<service_manager::mojom::Connector> receiver)163 void Clone(mojo::PendingReceiver<service_manager::mojom::Connector>
164 receiver) override {
165 AddReceiver(std::move(receiver));
166 }
167
168 ExternalMojoBroker::ConnectorImpl* const connector_;
169
170 mojo::ReceiverSet<service_manager::mojom::Connector> receivers_;
171 };
172
173 struct PendingBindRequest {
PendingBindRequestchromecast::external_mojo::ExternalMojoBroker::ConnectorImpl::PendingBindRequest174 PendingBindRequest(const std::string& interface_name,
175 mojo::ScopedMessagePipeHandle interface_pipe)
176 : interface_name(interface_name),
177 interface_pipe(std::move(interface_pipe)) {}
178
179 const std::string interface_name;
180 mojo::ScopedMessagePipeHandle interface_pipe;
181 };
182
RegisterExternalServices(const std::vector<std::string> & external_services_to_proxy)183 void RegisterExternalServices(
184 const std::vector<std::string>& external_services_to_proxy) {
185 if (external_services_to_proxy.empty()) {
186 return;
187 }
188
189 for (const auto& service_name : external_services_to_proxy) {
190 LOG(INFO) << "Register proxy for external " << service_name;
191 mojo::PendingRemote<service_manager::mojom::Service> service_remote;
192 registered_external_services_[service_name] =
193 std::make_unique<ExternalServiceProxy>(
194 this, service_name,
195 service_remote.InitWithNewPipeAndPassReceiver());
196
197 connector_->RegisterServiceInstance(
198 service_manager::Identity(service_name,
199 service_manager::kSystemInstanceGroup,
200 base::Token{}, base::Token::CreateRandom()),
201 std::move(service_remote),
202 mojo::NullReceiver() /* metadata_receiver */,
203 base::BindOnce(&OnRegisterServiceResult, service_name));
204 }
205 }
206
207 // Helper for ExternalServiceProxy.
BindExternalInterface(const std::string & service_name,const std::string & interface_name,mojo::ScopedMessagePipeHandle interface_pipe)208 void BindExternalInterface(const std::string& service_name,
209 const std::string& interface_name,
210 mojo::ScopedMessagePipeHandle interface_pipe) {
211 LOG(INFO) << "Internal request for " << service_name << ":"
212 << interface_name;
213 auto it = services_.find(service_name);
214 if (it != services_.end()) {
215 it->second->OnBindInterface(interface_name, std::move(interface_pipe));
216 return;
217 }
218
219 ServiceNotFound(service_name, interface_name, std::move(interface_pipe));
220 }
221
RegisterServiceInstance(const std::string & service_name,mojo::PendingRemote<mojom::ExternalService> service_remote)222 void RegisterServiceInstance(
223 const std::string& service_name,
224 mojo::PendingRemote<mojom::ExternalService> service_remote) {
225 if (services_.find(service_name) != services_.end()) {
226 LOG(ERROR) << "Duplicate service " << service_name;
227 return;
228 }
229 TRACE_EVENT_INSTANT1("mojom", "RegisterService", TRACE_EVENT_SCOPE_THREAD,
230 "service", service_name);
231 LOG(INFO) << "Register service " << service_name;
232 mojo::Remote<mojom::ExternalService> service(std::move(service_remote));
233 service.set_disconnect_handler(base::BindOnce(
234 &ConnectorImpl::OnServiceLost, base::Unretained(this), service_name));
235 auto it = services_.emplace(service_name, std::move(service)).first;
236
237 auto p = pending_bind_requests_.find(service_name);
238 if (p != pending_bind_requests_.end()) {
239 for (auto& request : p->second) {
240 it->second->OnBindInterface(request.interface_name,
241 std::move(request.interface_pipe));
242 }
243 pending_bind_requests_.erase(p);
244 }
245
246 auto& info_entry = services_info_[service_name];
247 info_entry.name = service_name;
248 info_entry.connect_time = base::TimeTicks::Now();
249 info_entry.disconnect_time = base::TimeTicks();
250 }
251
252 // standalone::mojom::Connector implementation:
RegisterServiceInstances(std::vector<chromecast::external_mojo::mojom::ServiceInstanceInfoPtr> service_instances_info)253 void RegisterServiceInstances(
254 std::vector<chromecast::external_mojo::mojom::ServiceInstanceInfoPtr>
255 service_instances_info) override {
256 for (auto& instance_info_ptr : service_instances_info) {
257 RegisterServiceInstance(instance_info_ptr->service_name,
258 std::move(instance_info_ptr->service_remote));
259 }
260 }
261
BindInterface(const std::string & service_name,const std::string & interface_name,mojo::ScopedMessagePipeHandle interface_pipe)262 void BindInterface(const std::string& service_name,
263 const std::string& interface_name,
264 mojo::ScopedMessagePipeHandle interface_pipe) override {
265 LOG(INFO) << "Request for " << service_name << ":" << interface_name;
266 TRACE_EVENT_INSTANT1("mojom", "BindToService", TRACE_EVENT_SCOPE_THREAD,
267 "service", service_name);
268 auto it = services_.find(service_name);
269 if (it != services_.end()) {
270 LOG(INFO) << "Found externally-registered " << service_name;
271 it->second->OnBindInterface(interface_name, std::move(interface_pipe));
272 return;
273 }
274
275 if (!connector_) {
276 ServiceNotFound(service_name, interface_name, std::move(interface_pipe));
277 return;
278 }
279
280 connector_->QueryService(
281 service_name,
282 base::BindOnce(&ConnectorImpl::OnQueryResult, base::Unretained(this),
283 service_name, interface_name,
284 std::move(interface_pipe)));
285 }
286
Clone(mojo::PendingReceiver<mojom::ExternalConnector> receiver)287 void Clone(
288 mojo::PendingReceiver<mojom::ExternalConnector> receiver) override {
289 AddReceiver(std::move(receiver));
290 }
291
BindChromiumConnector(mojo::ScopedMessagePipeHandle interface_pipe)292 void BindChromiumConnector(
293 mojo::ScopedMessagePipeHandle interface_pipe) override {
294 if (!connector_) {
295 connector_facade_.AddReceiver(
296 mojo::PendingReceiver<service_manager::mojom::Connector>(
297 std::move(interface_pipe)));
298 return;
299 }
300
301 connector_->BindConnectorReceiver(
302 mojo::PendingReceiver<service_manager::mojom::Connector>(
303 std::move(interface_pipe)));
304 }
305
QueryServiceList(QueryServiceListCallback callback)306 void QueryServiceList(QueryServiceListCallback callback) override {
307 std::vector<chromecast::external_mojo::mojom::ExternalServiceInfoPtr> infos;
308 for (const auto& it : services_info_) {
309 infos.emplace_back(it.second.Clone());
310 }
311 std::move(callback).Run(std::move(infos));
312 }
313
OnQueryResult(const std::string & service_name,const std::string & interface_name,mojo::ScopedMessagePipeHandle interface_pipe,service_manager::mojom::ServiceInfoPtr info)314 void OnQueryResult(const std::string& service_name,
315 const std::string& interface_name,
316 mojo::ScopedMessagePipeHandle interface_pipe,
317 service_manager::mojom::ServiceInfoPtr info) {
318 if (!info) {
319 ServiceNotFound(service_name, interface_name, std::move(interface_pipe));
320 return;
321 }
322
323 LOG(INFO) << "Found internal " << service_name;
324 connector_->BindInterface(
325 service_manager::ServiceFilter::ByName(service_name), interface_name,
326 std::move(interface_pipe),
327 service_manager::mojom::BindInterfacePriority::kImportant,
328 base::BindOnce(&OnInternalBindResult, service_name, interface_name));
329 }
330
ServiceNotFound(const std::string & service_name,const std::string & interface_name,mojo::ScopedMessagePipeHandle interface_pipe)331 void ServiceNotFound(const std::string& service_name,
332 const std::string& interface_name,
333 mojo::ScopedMessagePipeHandle interface_pipe) {
334 LOG(INFO) << service_name << " not found";
335 // Assume the service will be registered later, and wait until then.
336 pending_bind_requests_[service_name].emplace_back(
337 interface_name, std::move(interface_pipe));
338 }
339
OnServiceLost(const std::string & service_name)340 void OnServiceLost(const std::string& service_name) {
341 LOG(INFO) << service_name << " disconnected";
342 TRACE_EVENT_INSTANT1("mojom", "ServiceDisconnected",
343 TRACE_EVENT_SCOPE_THREAD, "service", service_name);
344 services_.erase(service_name);
345 services_info_[service_name].disconnect_time = base::TimeTicks::Now();
346 }
347
348 ServiceManagerConnectorFacade connector_facade_;
349 std::unique_ptr<service_manager::Connector> connector_;
350
351 mojo::ReceiverSet<mojom::ExternalConnector> receivers_;
352 std::map<std::string, std::unique_ptr<ExternalServiceProxy>>
353 registered_external_services_;
354
355 std::map<std::string, mojo::Remote<mojom::ExternalService>> services_;
356 std::map<std::string, std::vector<PendingBindRequest>> pending_bind_requests_;
357 std::map<std::string, mojom::ExternalServiceInfo> services_info_;
358
359 DISALLOW_COPY_AND_ASSIGN(ConnectorImpl);
360 };
361
362 class ExternalMojoBroker::ReadWatcher
363 : public base::MessagePumpForIO::FdWatcher {
364 public:
ReadWatcher(ConnectorImpl * connector,mojo::PlatformHandle listen_handle)365 ReadWatcher(ConnectorImpl* connector, mojo::PlatformHandle listen_handle)
366 : connector_(connector),
367 listen_handle_(std::move(listen_handle)),
368 watch_controller_(FROM_HERE) {
369 DCHECK(listen_handle_.is_valid());
370 base::CurrentIOThread::Get().WatchFileDescriptor(
371 listen_handle_.GetFD().get(), true /* persistent */,
372 base::MessagePumpForIO::WATCH_READ, &watch_controller_, this);
373 }
374
375 // base::MessagePumpForIO::FdWatcher implementation:
OnFileCanReadWithoutBlocking(int fd)376 void OnFileCanReadWithoutBlocking(int fd) override {
377 base::ScopedFD accepted_fd;
378 if (mojo::AcceptSocketConnection(fd, &accepted_fd,
379 false /* check_peer_user */) &&
380 accepted_fd.is_valid()) {
381 mojo::OutgoingInvitation invitation;
382 auto pipe = invitation.AttachMessagePipe(0);
383 mojo::OutgoingInvitation::Send(
384 std::move(invitation), base::kNullProcessHandle,
385 mojo::PlatformChannelEndpoint(
386 mojo::PlatformHandle(std::move(accepted_fd))));
387 connector_->AddReceiver(
388 mojo::PendingReceiver<mojom::ExternalConnector>(std::move(pipe)));
389 }
390 }
391
OnFileCanWriteWithoutBlocking(int)392 void OnFileCanWriteWithoutBlocking(int /* fd */) override {}
393
394 private:
395 ConnectorImpl* const connector_;
396 const mojo::PlatformHandle listen_handle_;
397 base::MessagePumpForIO::FdWatchController watch_controller_;
398
399 DISALLOW_COPY_AND_ASSIGN(ReadWatcher);
400 };
401
ExternalMojoBroker(const std::string & broker_path)402 ExternalMojoBroker::ExternalMojoBroker(const std::string& broker_path) {
403 connector_ = std::make_unique<ConnectorImpl>();
404
405 LOG(INFO) << "Initializing external mojo broker at: " << broker_path;
406
407 mojo::NamedPlatformChannel::Options channel_options;
408 channel_options.server_name = broker_path;
409 mojo::NamedPlatformChannel named_channel(channel_options);
410
411 mojo::PlatformChannelServerEndpoint server_endpoint =
412 named_channel.TakeServerEndpoint();
413 DCHECK(server_endpoint.is_valid());
414
415 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_BSD)
416 chmod(broker_path.c_str(), 0770);
417 #endif
418
419 read_watcher_ = std::make_unique<ReadWatcher>(
420 connector_.get(), server_endpoint.TakePlatformHandle());
421 }
422
InitializeChromium(std::unique_ptr<service_manager::Connector> connector,const std::vector<std::string> & external_services_to_proxy)423 void ExternalMojoBroker::InitializeChromium(
424 std::unique_ptr<service_manager::Connector> connector,
425 const std::vector<std::string>& external_services_to_proxy) {
426 connector_->InitializeChromium(std::move(connector),
427 external_services_to_proxy);
428 }
429
430 mojo::PendingRemote<mojom::ExternalConnector>
CreateConnector()431 ExternalMojoBroker::CreateConnector() {
432 mojo::PendingRemote<mojom::ExternalConnector> remote;
433 connector_->AddReceiver(remote.InitWithNewPipeAndPassReceiver());
434 return remote;
435 }
436
437 ExternalMojoBroker::~ExternalMojoBroker() = default;
438
439 } // namespace external_mojo
440 } // namespace chromecast
441