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