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