1 /*
2 * Copyright 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "hci/facade/le_acl_manager_facade.h"
18
19 #include <condition_variable>
20 #include <memory>
21 #include <mutex>
22
23 #include "blueberry/facade/hci/le_acl_manager_facade.grpc.pb.h"
24 #include "blueberry/facade/hci/le_acl_manager_facade.pb.h"
25 #include "common/bind.h"
26 #include "grpc/grpc_event_queue.h"
27 #include "hci/acl_manager.h"
28 #include "hci/hci_packets.h"
29 #include "packet/raw_builder.h"
30
31 using ::grpc::ServerAsyncResponseWriter;
32 using ::grpc::ServerAsyncWriter;
33 using ::grpc::ServerContext;
34
35 using ::bluetooth::packet::RawBuilder;
36
37 namespace bluetooth {
38 namespace hci {
39 namespace facade {
40
41 using acl_manager::LeAclConnection;
42 using acl_manager::LeConnectionCallbacks;
43 using acl_manager::LeConnectionManagementCallbacks;
44
45 using namespace blueberry::facade::hci;
46
47 class LeAclManagerFacadeService : public LeAclManagerFacade::Service, public LeConnectionCallbacks {
48 public:
LeAclManagerFacadeService(AclManager * acl_manager,::bluetooth::os::Handler * facade_handler)49 LeAclManagerFacadeService(AclManager* acl_manager, ::bluetooth::os::Handler* facade_handler)
50 : acl_manager_(acl_manager), facade_handler_(facade_handler) {
51 acl_manager_->RegisterLeCallbacks(this, facade_handler_);
52 }
53
~LeAclManagerFacadeService()54 ~LeAclManagerFacadeService() {
55 std::unique_lock<std::mutex> lock(acl_connections_mutex_);
56 for (auto& conn : acl_connections_) {
57 if (conn.second.connection_ != nullptr) {
58 conn.second.connection_->GetAclQueueEnd()->UnregisterDequeue();
59 conn.second.connection_.reset();
60 }
61 }
62 }
63
CreateConnection(::grpc::ServerContext * context,const CreateConnectionMsg * request,::grpc::ServerWriter<LeConnectionEvent> * writer)64 ::grpc::Status CreateConnection(
65 ::grpc::ServerContext* context,
66 const CreateConnectionMsg* request,
67 ::grpc::ServerWriter<LeConnectionEvent>* writer) override {
68 LOG_INFO(
69 "peer=%s, type=%d, id_direct=%d",
70 request->peer_address().address().address().c_str(),
71 request->peer_address().type(),
72 request->is_direct());
73 Address peer_address;
74 ASSERT(Address::FromString(request->peer_address().address().address(), peer_address));
75 AddressWithType peer(peer_address, static_cast<AddressType>(request->peer_address().type()));
76 bool is_direct = request->is_direct();
77 acl_manager_->CreateLeConnection(peer, is_direct);
78
79 if (is_direct) {
80 if (direct_connection_events_ != nullptr) {
81 return ::grpc::Status(
82 ::grpc::StatusCode::RESOURCE_EXHAUSTED, "Only one outstanding direct request is supported");
83 }
84 direct_connection_events_ = std::make_shared<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>>(
85 std::string("direct connection attempt ") + peer.ToString());
86 direct_connection_address_ = peer;
87 return direct_connection_events_->RunLoop(context, writer);
88 }
89 per_connection_events_.emplace(
90 peer,
91 std::make_unique<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>>(
92 std::string("connection attempt ") + peer.ToString()));
93 return per_connection_events_[peer]->RunLoop(context, writer);
94 }
95
CancelConnection(::grpc::ServerContext * context,const::blueberry::facade::BluetoothAddressWithType * request,google::protobuf::Empty * response)96 ::grpc::Status CancelConnection(
97 ::grpc::ServerContext* context,
98 const ::blueberry::facade::BluetoothAddressWithType* request,
99 google::protobuf::Empty* response) override {
100 LOG_INFO("peer=%s, type=%d", request->address().address().c_str(), request->type());
101 Address peer_address;
102 ASSERT(Address::FromString(request->address().address(), peer_address));
103 AddressWithType peer(peer_address, static_cast<AddressType>(request->type()));
104 if (peer == direct_connection_address_) {
105 direct_connection_address_ = AddressWithType();
106 direct_connection_events_.reset();
107 } else {
108 if (per_connection_events_.count(peer) == 0) {
109 return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "No matching outstanding connection");
110 }
111 }
112 acl_manager_->CancelLeConnect(peer);
113 return ::grpc::Status::OK;
114 }
115
Disconnect(::grpc::ServerContext * context,const LeHandleMsg * request,::google::protobuf::Empty * response)116 ::grpc::Status Disconnect(::grpc::ServerContext* context, const LeHandleMsg* request,
117 ::google::protobuf::Empty* response) override {
118 LOG_INFO("handle=%d", request->handle());
119 std::unique_lock<std::mutex> lock(acl_connections_mutex_);
120 auto connection = acl_connections_.find(request->handle());
121 if (connection == acl_connections_.end()) {
122 LOG_ERROR("Invalid handle");
123 return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle");
124 } else {
125 connection->second.connection_->Disconnect(DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION);
126 return ::grpc::Status::OK;
127 }
128 }
129
130 #define GET_CONNECTION(view) \
131 std::map<uint16_t, Connection>::iterator connection; \
132 do { \
133 if (!view.IsValid()) { \
134 return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle"); \
135 } \
136 std::unique_lock<std::mutex> lock(acl_connections_mutex_); \
137 connection = acl_connections_.find(view.GetConnectionHandle()); \
138 if (connection == acl_connections_.end()) { \
139 return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle"); \
140 } \
141 } while (0)
142
ConnectionCommand(::grpc::ServerContext * context,const LeConnectionCommandMsg * request,::google::protobuf::Empty * response)143 ::grpc::Status ConnectionCommand(
144 ::grpc::ServerContext* context,
145 const LeConnectionCommandMsg* request,
146 ::google::protobuf::Empty* response) override {
147 LOG_INFO("size=%zu", request->packet().size());
148 auto command_view =
149 ConnectionManagementCommandView::Create(AclCommandView::Create(CommandView::Create(PacketView<kLittleEndian>(
150 std::make_shared<std::vector<uint8_t>>(request->packet().begin(), request->packet().end())))));
151 if (!command_view.IsValid()) {
152 return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid command packet");
153 }
154 LOG_INFO("opcode=%s", OpCodeText(command_view.GetOpCode()).c_str());
155 switch (command_view.GetOpCode()) {
156 case OpCode::DISCONNECT: {
157 auto view = DisconnectView::Create(command_view);
158 GET_CONNECTION(view);
159 connection->second.connection_->Disconnect(view.GetReason());
160 return ::grpc::Status::OK;
161 }
162 default:
163 return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid command packet");
164 }
165 }
166 #undef GET_CONNECTION
167
FetchIncomingConnection(::grpc::ServerContext * context,const google::protobuf::Empty * request,::grpc::ServerWriter<LeConnectionEvent> * writer)168 ::grpc::Status FetchIncomingConnection(
169 ::grpc::ServerContext* context,
170 const google::protobuf::Empty* request,
171 ::grpc::ServerWriter<LeConnectionEvent>* writer) override {
172 LOG_INFO("wait for one incoming connection");
173 if (incoming_connection_events_ != nullptr) {
174 return ::grpc::Status(
175 ::grpc::StatusCode::RESOURCE_EXHAUSTED, "Only one outstanding incoming connection is supported");
176 }
177 incoming_connection_events_ =
178 std::make_unique<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>>(std::string("incoming connection "));
179 return incoming_connection_events_->RunLoop(context, writer);
180 }
181
AddDeviceToResolvingList(::grpc::ServerContext * context,const IrkMsg * request,::google::protobuf::Empty * response)182 ::grpc::Status AddDeviceToResolvingList(
183 ::grpc::ServerContext* context, const IrkMsg* request, ::google::protobuf::Empty* response) override {
184 LOG_INFO("peer=%s, type=%d", request->peer().address().address().c_str(), request->peer().type());
185 Address peer_address;
186 ASSERT(Address::FromString(request->peer().address().address(), peer_address));
187 AddressWithType peer(peer_address, static_cast<AddressType>(request->peer().type()));
188
189 auto request_peer_irk_length = request->peer_irk().end() - request->peer_irk().begin();
190
191 if (request_peer_irk_length != crypto_toolbox::OCTET16_LEN) {
192 return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid Peer IRK");
193 }
194
195 auto request_local_irk_length = request->local_irk().end() - request->local_irk().begin();
196 if (request_local_irk_length != crypto_toolbox::OCTET16_LEN) {
197 return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid Local IRK");
198 }
199
200 crypto_toolbox::Octet16 peer_irk = {};
201 crypto_toolbox::Octet16 local_irk = {};
202
203 std::vector<uint8_t> peer_irk_data(request->peer_irk().begin(), request->peer_irk().end());
204 std::copy_n(peer_irk_data.begin(), crypto_toolbox::OCTET16_LEN, peer_irk.begin());
205
206 std::vector<uint8_t> local_irk_data(request->local_irk().begin(), request->local_irk().end());
207 std::copy_n(local_irk_data.begin(), crypto_toolbox::OCTET16_LEN, local_irk.begin());
208
209 acl_manager_->AddDeviceToResolvingList(peer, peer_irk, local_irk);
210 return ::grpc::Status::OK;
211 }
212
SendAclData(::grpc::ServerContext * context,const LeAclData * request,::google::protobuf::Empty * response)213 ::grpc::Status SendAclData(
214 ::grpc::ServerContext* context, const LeAclData* request, ::google::protobuf::Empty* response) override {
215 LOG_INFO("handle=%d, size=%zu", request->handle(), request->payload().size());
216 std::promise<void> promise;
217 auto future = promise.get_future();
218 {
219 std::unique_lock<std::mutex> lock(acl_connections_mutex_);
220 auto connection = acl_connections_.find(request->handle());
221 if (connection == acl_connections_.end()) {
222 return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle");
223 }
224 connection->second.connection_->GetAclQueueEnd()->RegisterEnqueue(
225 facade_handler_,
226 common::Bind(
227 &LeAclManagerFacadeService::enqueue_packet,
228 common::Unretained(this),
229 common::Unretained(request),
230 common::Passed(std::move(promise))));
231 auto status = future.wait_for(std::chrono::milliseconds(1000));
232 if (status != std::future_status::ready) {
233 return ::grpc::Status(::grpc::StatusCode::RESOURCE_EXHAUSTED, "Can't send packet");
234 }
235 }
236 return ::grpc::Status::OK;
237 }
238
enqueue_packet(const LeAclData * request,std::promise<void> promise)239 std::unique_ptr<BasePacketBuilder> enqueue_packet(const LeAclData* request, std::promise<void> promise) {
240 auto connection = acl_connections_.find(request->handle());
241 ASSERT_LOG(connection != acl_connections_.end(), "handle %d", request->handle());
242 connection->second.connection_->GetAclQueueEnd()->UnregisterEnqueue();
243 std::unique_ptr<RawBuilder> packet =
244 std::make_unique<RawBuilder>(std::vector<uint8_t>(request->payload().begin(), request->payload().end()));
245 promise.set_value();
246 return packet;
247 }
248
FetchAclData(::grpc::ServerContext * context,const LeHandleMsg * request,::grpc::ServerWriter<LeAclData> * writer)249 ::grpc::Status FetchAclData(
250 ::grpc::ServerContext* context, const LeHandleMsg* request, ::grpc::ServerWriter<LeAclData>* writer) override {
251 LOG_INFO("handle=%d", request->handle());
252 auto connection = acl_connections_.find(request->handle());
253 if (connection == acl_connections_.end()) {
254 return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle");
255 }
256 return connection->second.pending_acl_data_.RunLoop(context, writer);
257 }
258
builder_to_string(std::unique_ptr<BasePacketBuilder> builder)259 static inline std::string builder_to_string(std::unique_ptr<BasePacketBuilder> builder) {
260 std::vector<uint8_t> bytes;
261 BitInserter bit_inserter(bytes);
262 builder->Serialize(bit_inserter);
263 return std::string(bytes.begin(), bytes.end());
264 }
265
on_incoming_acl(std::shared_ptr<LeAclConnection> connection,uint16_t handle)266 void on_incoming_acl(std::shared_ptr<LeAclConnection> connection, uint16_t handle) {
267 LOG_INFO("handle=%d, addr=%s", connection->GetHandle(),
268 ADDRESS_TO_LOGGABLE_CSTR(connection->GetRemoteAddress()));
269 auto packet = connection->GetAclQueueEnd()->TryDequeue();
270 auto connection_tracker = acl_connections_.find(handle);
271 ASSERT_LOG(connection_tracker != acl_connections_.end(), "handle %d", handle);
272 LeAclData acl_data;
273 acl_data.set_handle(handle);
274 acl_data.set_payload(std::string(packet->begin(), packet->end()));
275 connection_tracker->second.pending_acl_data_.OnIncomingEvent(acl_data);
276 }
277
OnLeConnectSuccess(AddressWithType peer,std::unique_ptr<LeAclConnection> connection)278 void OnLeConnectSuccess(AddressWithType peer, std::unique_ptr<LeAclConnection> connection) override {
279 LOG_INFO("handle=%d, addr=%s", connection->GetHandle(),
280 ADDRESS_TO_LOGGABLE_CSTR(peer));
281 std::unique_lock<std::mutex> lock(acl_connections_mutex_);
282 std::shared_ptr<LeAclConnection> shared_connection = std::move(connection);
283 uint16_t handle = shared_connection->GetHandle();
284 auto role = shared_connection->GetRole();
285 if (role == Role::PERIPHERAL) {
286 ASSERT(incoming_connection_events_ != nullptr);
287 if (per_connection_events_.find(peer) == per_connection_events_.end()) {
288 per_connection_events_.emplace(peer, incoming_connection_events_);
289 } else {
290 per_connection_events_[peer] = incoming_connection_events_;
291 }
292 incoming_connection_events_.reset();
293 } else if (direct_connection_address_ == peer) {
294 direct_connection_address_ = AddressWithType();
295 per_connection_events_.emplace(peer, direct_connection_events_);
296 direct_connection_events_.reset();
297 } else {
298 ASSERT_LOG(
299 per_connection_events_.count(peer) > 0,
300 "No connection request for %s", ADDRESS_TO_LOGGABLE_CSTR(peer));
301 }
302 acl_connections_.erase(handle);
303 acl_connections_.emplace(
304 std::piecewise_construct,
305 std::forward_as_tuple(handle),
306 std::forward_as_tuple(handle, shared_connection, per_connection_events_[peer]));
307 shared_connection->GetAclQueueEnd()->RegisterDequeue(
308 facade_handler_,
309 common::Bind(&LeAclManagerFacadeService::on_incoming_acl, common::Unretained(this), shared_connection, handle));
310 auto callbacks = acl_connections_.find(handle)->second.GetCallbacks();
311 shared_connection->RegisterCallbacks(callbacks, facade_handler_);
312 {
313 std::unique_ptr<BasePacketBuilder> builder = LeConnectionCompleteBuilder::Create(
314 ErrorCode::SUCCESS, handle, role, peer.GetAddressType(), peer.GetAddress(), 1, 2, 3, ClockAccuracy::PPM_20);
315 LeConnectionEvent success;
316 success.set_payload(builder_to_string(std::move(builder)));
317 per_connection_events_[peer]->OnIncomingEvent(success);
318 }
319 }
320
OnLeConnectFail(AddressWithType address,ErrorCode reason)321 void OnLeConnectFail(AddressWithType address, ErrorCode reason) override {
322 LOG_INFO("addr=%s, reason=%s",
323 ADDRESS_TO_LOGGABLE_CSTR(address), ErrorCodeText(reason).c_str());
324 std::unique_ptr<BasePacketBuilder> builder = LeConnectionCompleteBuilder::Create(
325 reason, 0, Role::CENTRAL, address.GetAddressType(), address.GetAddress(), 0, 0, 0, ClockAccuracy::PPM_20);
326 LeConnectionEvent fail;
327 fail.set_payload(builder_to_string(std::move(builder)));
328 if (address == direct_connection_address_) {
329 direct_connection_address_ = AddressWithType();
330 direct_connection_events_->OnIncomingEvent(fail);
331 } else {
332 per_connection_events_[address]->OnIncomingEvent(fail);
333 }
334 }
335
336 class Connection : public LeConnectionManagementCallbacks {
337 public:
Connection(uint16_t handle,std::shared_ptr<LeAclConnection> connection,std::shared_ptr<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>> event_stream)338 Connection(
339 uint16_t handle,
340 std::shared_ptr<LeAclConnection> connection,
341 std::shared_ptr<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>> event_stream)
342 : handle_(handle), connection_(std::move(connection)), event_stream_(std::move(event_stream)) {}
OnConnectionUpdate(hci::ErrorCode hci_status,uint16_t connection_interval,uint16_t connection_latency,uint16_t supervision_timeout)343 void OnConnectionUpdate(
344 hci::ErrorCode hci_status,
345 uint16_t connection_interval,
346 uint16_t connection_latency,
347 uint16_t supervision_timeout) override {
348 LOG_INFO(
349 "interval: 0x%hx, latency: 0x%hx, timeout 0x%hx",
350 connection_interval,
351 connection_latency,
352 supervision_timeout);
353 }
354
OnDataLengthChange(uint16_t tx_octets,uint16_t tx_time,uint16_t rx_octets,uint16_t rx_time)355 void OnDataLengthChange(uint16_t tx_octets, uint16_t tx_time, uint16_t rx_octets, uint16_t rx_time) override {
356 LOG_INFO(
357 "tx_octets: 0x%hx, tx_time: 0x%hx, rx_octets 0x%hx, rx_time 0x%hx", tx_octets, tx_time, rx_octets, rx_time);
358 }
359
OnPhyUpdate(hci::ErrorCode hci_status,uint8_t tx_phy,uint8_t rx_phy)360 void OnPhyUpdate(hci::ErrorCode hci_status, uint8_t tx_phy, uint8_t rx_phy) override {}
OnDisconnection(ErrorCode reason)361 void OnDisconnection(ErrorCode reason) override {
362 LOG_INFO("reason: %s", ErrorCodeText(reason).c_str());
363 std::unique_ptr<BasePacketBuilder> builder =
364 DisconnectionCompleteBuilder::Create(ErrorCode::SUCCESS, handle_, reason);
365 LeConnectionEvent disconnection;
366 disconnection.set_payload(builder_to_string(std::move(builder)));
367 event_stream_->OnIncomingEvent(disconnection);
368 }
369
OnReadRemoteVersionInformationComplete(hci::ErrorCode hci_status,uint8_t lmp_version,uint16_t manufacturer_name,uint16_t sub_version)370 void OnReadRemoteVersionInformationComplete(
371 hci::ErrorCode hci_status, uint8_t lmp_version, uint16_t manufacturer_name, uint16_t sub_version) override {}
OnLeReadRemoteFeaturesComplete(hci::ErrorCode hci_status,uint64_t features)372 void OnLeReadRemoteFeaturesComplete(hci::ErrorCode hci_status, uint64_t features) override {}
373
GetCallbacks()374 LeConnectionManagementCallbacks* GetCallbacks() {
375 return this;
376 }
OnLeSubrateChange(hci::ErrorCode hci_status,uint16_t subrate_factor,uint16_t peripheral_latency,uint16_t continuation_number,uint16_t supervision_timeout)377 void OnLeSubrateChange(
378 hci::ErrorCode hci_status,
379 uint16_t subrate_factor,
380 uint16_t peripheral_latency,
381 uint16_t continuation_number,
382 uint16_t supervision_timeout) override {
383 LOG_INFO(
384 "hci_status: %s, subrate_factor: %#hx, peripheral_latency: %#hx, continuation_number: %#hx, "
385 "supervision_timeout: %#hx",
386 ErrorCodeText(hci_status).c_str(),
387 subrate_factor,
388 peripheral_latency,
389 continuation_number,
390 supervision_timeout);
391 }
392
393 uint16_t handle_;
394 std::shared_ptr<LeAclConnection> connection_;
395 std::shared_ptr<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>> event_stream_;
396 ::bluetooth::grpc::GrpcEventQueue<LeAclData> pending_acl_data_{std::string("PendingAclData") +
397 std::to_string(handle_)};
398 };
399
IsOnBackgroundList(::grpc::ServerContext * context,const::blueberry::facade::hci::BackgroundRequestMsg * request,::blueberry::facade::hci::BackgroundResultMsg * msg)400 ::grpc::Status IsOnBackgroundList(
401 ::grpc::ServerContext* context,
402 const ::blueberry::facade::hci::BackgroundRequestMsg* request,
403 ::blueberry::facade::hci::BackgroundResultMsg* msg) {
404 Address peer_address;
405 ASSERT(Address::FromString(request->peer_address().address().address(), peer_address));
406 AddressWithType peer(peer_address, static_cast<AddressType>(request->peer_address().type()));
407 std::promise<bool> promise;
408 auto future = promise.get_future();
409 acl_manager_->IsOnBackgroundList(peer, std::move(promise));
410 msg->set_is_on_background_list(future.get());
411 return ::grpc::Status::OK;
412 }
413
RemoveFromBackgroundList(::grpc::ServerContext * context,const::blueberry::facade::hci::BackgroundRequestMsg * request,::google::protobuf::Empty * response)414 ::grpc::Status RemoveFromBackgroundList(
415 ::grpc::ServerContext* context,
416 const ::blueberry::facade::hci::BackgroundRequestMsg* request,
417 ::google::protobuf::Empty* response) {
418 Address peer_address;
419 ASSERT(Address::FromString(request->peer_address().address().address(), peer_address));
420 AddressWithType peer(peer_address, static_cast<AddressType>(request->peer_address().type()));
421 acl_manager_->RemoveFromBackgroundList(peer);
422 return ::grpc::Status::OK;
423 }
424
425 private:
426 AclManager* acl_manager_;
427 ::bluetooth::os::Handler* facade_handler_;
428 mutable std::mutex acl_connections_mutex_;
429 std::map<bluetooth::hci::AddressWithType, std::shared_ptr<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>>>
430 per_connection_events_;
431 std::shared_ptr<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>> direct_connection_events_;
432 bluetooth::hci::AddressWithType direct_connection_address_;
433 std::shared_ptr<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>> incoming_connection_events_;
434 std::map<uint16_t, Connection> acl_connections_;
435 };
436
ListDependencies(ModuleList * list) const437 void LeAclManagerFacadeModule::ListDependencies(ModuleList* list) const {
438 ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
439 list->add<AclManager>();
440 }
441
Start()442 void LeAclManagerFacadeModule::Start() {
443 ::bluetooth::grpc::GrpcFacadeModule::Start();
444 service_ = new LeAclManagerFacadeService(GetDependency<AclManager>(), GetHandler());
445 }
446
Stop()447 void LeAclManagerFacadeModule::Stop() {
448 delete service_;
449 ::bluetooth::grpc::GrpcFacadeModule::Stop();
450 }
451
GetService() const452 ::grpc::Service* LeAclManagerFacadeModule::GetService() const {
453 return service_;
454 }
455
456 const ModuleFactory LeAclManagerFacadeModule::Factory =
__anon239a8f540102() 457 ::bluetooth::ModuleFactory([]() { return new LeAclManagerFacadeModule(); });
458
459 } // namespace facade
460 } // namespace hci
461 } // namespace bluetooth
462