• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef BASE_FUCHSIA_SCOPED_SERVICE_BINDING_H_
6 #define BASE_FUCHSIA_SCOPED_SERVICE_BINDING_H_
7 
8 #include <utility>
9 
10 // TODO(crbug.com/1427626): Remove this include once the explicit
11 // async_get_default_dispatcher() is no longer needed.
12 #include <lib/async/default.h>
13 #include <lib/fidl/cpp/binding.h>
14 #include <lib/fidl/cpp/binding_set.h>
15 #include <lib/fidl/cpp/interface_request.h>
16 #include <lib/fidl/cpp/wire/connect_service.h>
17 #include <lib/zx/channel.h>
18 
19 #include "base/base_export.h"
20 #include "base/fuchsia/scoped_service_publisher.h"
21 #include "base/functional/callback.h"
22 #include "base/strings/string_piece_forward.h"
23 #include "third_party/abseil-cpp/absl/types/optional.h"
24 
25 namespace sys {
26 class OutgoingDirectory;
27 }  // namespace sys
28 
29 namespace vfs {
30 class PseudoDir;
31 }  // namespace vfs
32 
33 namespace base {
34 
35 template <typename Interface>
36 class BASE_EXPORT ScopedServiceBinding {
37  public:
38   // Publishes a public service in the specified |outgoing_directory|.
39   // |outgoing_directory| and |impl| must outlive the binding. The service is
40   // unpublished on destruction.
41   ScopedServiceBinding(sys::OutgoingDirectory* outgoing_directory,
42                        Interface* impl,
43                        base::StringPiece name = Interface::Name_)
44       : publisher_(outgoing_directory, bindings_.GetHandler(impl), name) {}
45 
46   // Publishes a service in the specified |pseudo_dir|. |pseudo_dir| and |impl|
47   // must outlive the binding. The service is unpublished on destruction.
48   ScopedServiceBinding(vfs::PseudoDir* pseudo_dir, Interface* impl,
49                        base::StringPiece name = Interface::Name_)
50       : publisher_(pseudo_dir, bindings_.GetHandler(impl), name) {}
51 
52   ScopedServiceBinding(const ScopedServiceBinding&) = delete;
53   ScopedServiceBinding& operator=(const ScopedServiceBinding&) = delete;
54 
55   ~ScopedServiceBinding() = default;
56 
57   // |on_last_client_callback| will be called every time the number of connected
58   // clients drops to 0.
SetOnLastClientCallback(base::RepeatingClosure on_last_client_callback)59   void SetOnLastClientCallback(base::RepeatingClosure on_last_client_callback) {
60     bindings_.set_empty_set_handler(
61         [callback = std::move(on_last_client_callback)] { callback.Run(); });
62   }
63 
has_clients()64   bool has_clients() const { return bindings_.size() != 0; }
65 
66  private:
67   fidl::BindingSet<Interface> bindings_;
68   ScopedServicePublisher<Interface> publisher_;
69 };
70 
71 template <typename Protocol>
72 class BASE_EXPORT ScopedNaturalServiceBinding {
73  public:
74   // Publishes a public service in the specified |outgoing_directory|.
75   // |outgoing_directory| and |impl| must outlive the binding. The service is
76   // unpublished on destruction.
77   ScopedNaturalServiceBinding(
78       sys::OutgoingDirectory* outgoing_directory,
79       fidl::Server<Protocol>* impl,
80       base::StringPiece name = fidl::DiscoverableProtocolName<Protocol>)
81       : publisher_(
82             outgoing_directory,
83             bindings_.CreateHandler(
84                 impl,
85                 // TODO(crbug.com/1427626): Remove this param once there's an
86                 // overload of `CreateHandler` that doesn't require it.
87                 async_get_default_dispatcher(),
88                 [](fidl::UnbindInfo info) {}),
89             name) {}
90 
91   // Publishes a service in the specified |pseudo_dir|. |pseudo_dir| and |impl|
92   // must outlive the binding. The service is unpublished on destruction.
93   ScopedNaturalServiceBinding(
94       vfs::PseudoDir* pseudo_dir,
95       fidl::Server<Protocol>* impl,
96       base::StringPiece name = fidl::DiscoverableProtocolName<Protocol>)
97       : publisher_(
98             pseudo_dir,
99             bindings_.CreateHandler(
100                 impl,
101                 // TODO(crbug.com/1427626): Remove this param once there's an
102                 // overload of `CreateHandler` that doesn't require it.
103                 async_get_default_dispatcher(),
104                 [](fidl::UnbindInfo info) {}),
105             name) {}
106 
107   ScopedNaturalServiceBinding(const ScopedNaturalServiceBinding&) = delete;
108   ScopedNaturalServiceBinding& operator=(const ScopedNaturalServiceBinding&) =
109       delete;
110 
111   ~ScopedNaturalServiceBinding() = default;
112 
113   // Registers `on_last_client_callback` to be called every time the number of
114   // connected clients drops to 0.
SetOnLastClientCallback(base::RepeatingClosure on_last_client_callback)115   void SetOnLastClientCallback(base::RepeatingClosure on_last_client_callback) {
116     bindings_.set_empty_set_handler(
117         [callback = std::move(on_last_client_callback)] { callback.Run(); });
118   }
119 
has_clients()120   bool has_clients() const { return bindings_.size() != 0; }
121 
122  private:
123   fidl::ServerBindingGroup<Protocol> bindings_;
124   ScopedNaturalServicePublisher<Protocol> publisher_;
125 };
126 
127 // Scoped service binding which allows only a single client to be connected
128 // at any time. By default a new connection will disconnect an existing client.
129 enum class ScopedServiceBindingPolicy {
130   kPreferNew,
131   kPreferExisting,
132   kConnectOnce
133 };
134 
135 template <typename Interface,
136           ScopedServiceBindingPolicy Policy =
137               ScopedServiceBindingPolicy::kPreferNew>
138 class BASE_EXPORT ScopedSingleClientServiceBinding {
139  public:
140   // |outgoing_directory| and |impl| must outlive the binding.
141   ScopedSingleClientServiceBinding(sys::OutgoingDirectory* outgoing_directory,
142                                    Interface* impl,
143                                    base::StringPiece name = Interface::Name_)
binding_(impl)144       : binding_(impl) {
145     publisher_.emplace(
146         outgoing_directory,
147         fit::bind_member(this, &ScopedSingleClientServiceBinding::BindClient),
148         name);
149     binding_.set_error_handler(fit::bind_member(
150         this, &ScopedSingleClientServiceBinding::OnBindingEmpty));
151   }
152 
153   ScopedSingleClientServiceBinding(vfs::PseudoDir* publish_to,
154                                    Interface* impl,
155                                    base::StringPiece name = Interface::Name_)
binding_(impl)156       : binding_(impl) {
157     publisher_.emplace(
158         publish_to,
159         fit::bind_member(this, &ScopedSingleClientServiceBinding::BindClient),
160         name);
161     binding_.set_error_handler(fit::bind_member(
162         this, &ScopedSingleClientServiceBinding::OnBindingEmpty));
163   }
164 
165   ScopedSingleClientServiceBinding(const ScopedSingleClientServiceBinding&) =
166       delete;
167   ScopedSingleClientServiceBinding& operator=(
168       const ScopedSingleClientServiceBinding&) = delete;
169 
170   ~ScopedSingleClientServiceBinding() = default;
171 
events()172   typename Interface::EventSender_& events() { return binding_.events(); }
173 
174   // |on_last_client_callback| will be called the first time a client
175   // disconnects. It is still  possible for a client to connect after that point
176   // if Policy is kPreferNew of kPreferExisting.
SetOnLastClientCallback(base::OnceClosure on_last_client_callback)177   void SetOnLastClientCallback(base::OnceClosure on_last_client_callback) {
178     on_last_client_callback_ = std::move(on_last_client_callback);
179   }
180 
has_clients()181   bool has_clients() const { return binding_.is_bound(); }
182 
183  private:
BindClient(fidl::InterfaceRequest<Interface> request)184   void BindClient(fidl::InterfaceRequest<Interface> request) {
185     if (Policy == ScopedServiceBindingPolicy::kPreferExisting &&
186         binding_.is_bound()) {
187       return;
188     }
189     binding_.Bind(std::move(request));
190     if (Policy == ScopedServiceBindingPolicy::kConnectOnce) {
191       publisher_.reset();
192     }
193   }
194 
OnBindingEmpty(zx_status_t status)195   void OnBindingEmpty(zx_status_t status) {
196     if (on_last_client_callback_) {
197       std::move(on_last_client_callback_).Run();
198     }
199   }
200 
201   fidl::Binding<Interface> binding_;
202   absl::optional<ScopedServicePublisher<Interface>> publisher_;
203   base::OnceClosure on_last_client_callback_;
204 };
205 
206 }  // namespace base
207 
208 #endif  // BASE_FUCHSIA_SCOPED_SERVICE_BINDING_H_
209