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