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