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 "l2cap/classic/facade.h"
18
19 #include <bluetooth/log.h>
20
21 #include <condition_variable>
22 #include <cstdint>
23 #include <unordered_map>
24
25 #include "blueberry/facade/l2cap/classic/facade.grpc.pb.h"
26 #include "common/bidi_queue.h"
27 #include "common/bind.h"
28 #include "common/callback.h"
29 #include "grpc/grpc_event_queue.h"
30 #include "hci/address.h"
31 #include "l2cap/classic/l2cap_classic_module.h"
32 #include "os/log.h"
33 #include "packet/raw_builder.h"
34
35 using ::grpc::ServerAsyncResponseWriter;
36 using ::grpc::ServerAsyncWriter;
37 using ::grpc::ServerContext;
38
39 using ::bluetooth::packet::RawBuilder;
40
41 namespace bluetooth {
42 namespace l2cap {
43 namespace classic {
44
45 using namespace blueberry::facade::l2cap::classic;
46
47 class L2capClassicModuleFacadeService : public L2capClassicModuleFacade::Service, public LinkSecurityInterfaceListener {
48 public:
L2capClassicModuleFacadeService(L2capClassicModule * l2cap_layer,os::Handler * facade_handler)49 L2capClassicModuleFacadeService(L2capClassicModule* l2cap_layer, os::Handler* facade_handler)
50 : l2cap_layer_(l2cap_layer), facade_handler_(facade_handler), security_interface_(nullptr) {
51 log::assert_that(l2cap_layer_ != nullptr, "assert failed: l2cap_layer_ != nullptr");
52 log::assert_that(facade_handler_ != nullptr, "assert failed: facade_handler_ != nullptr");
53 }
54
FetchConnectionComplete(::grpc::ServerContext * context,const::google::protobuf::Empty *,::grpc::ServerWriter<classic::ConnectionCompleteEvent> * writer)55 ::grpc::Status FetchConnectionComplete(
56 ::grpc::ServerContext* context,
57 const ::google::protobuf::Empty* /* request */,
58 ::grpc::ServerWriter<classic::ConnectionCompleteEvent>* writer) override {
59 return pending_connection_complete_.RunLoop(context, writer);
60 }
61
FetchConnectionClose(::grpc::ServerContext * context,const::google::protobuf::Empty *,::grpc::ServerWriter<classic::ConnectionCloseEvent> * writer)62 ::grpc::Status FetchConnectionClose(
63 ::grpc::ServerContext* context,
64 const ::google::protobuf::Empty* /* request */,
65 ::grpc::ServerWriter<classic::ConnectionCloseEvent>* writer) override {
66 return pending_connection_close_.RunLoop(context, writer);
67 }
68
SendDynamicChannelPacket(::grpc::ServerContext *,const DynamicChannelPacket * request,::google::protobuf::Empty *)69 ::grpc::Status SendDynamicChannelPacket(
70 ::grpc::ServerContext* /* context */,
71 const DynamicChannelPacket* request,
72 ::google::protobuf::Empty* /* response */) override {
73 std::unique_lock<std::mutex> lock(channel_map_mutex_);
74 if (dynamic_channel_helper_map_.find(request->psm()) == dynamic_channel_helper_map_.end()) {
75 return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered");
76 }
77 std::vector<uint8_t> packet(request->payload().begin(), request->payload().end());
78 if (!dynamic_channel_helper_map_[request->psm()]->SendPacket(packet)) {
79 return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Channel not open");
80 }
81 return ::grpc::Status::OK;
82 }
83
OpenChannel(::grpc::ServerContext *,const::bluetooth::l2cap::classic::OpenChannelRequest * request,::google::protobuf::Empty *)84 ::grpc::Status OpenChannel(
85 ::grpc::ServerContext* /* context */,
86 const ::bluetooth::l2cap::classic::OpenChannelRequest* request,
87 ::google::protobuf::Empty* /* response */) override {
88 auto service_helper = dynamic_channel_helper_map_.find(request->psm());
89 if (service_helper == dynamic_channel_helper_map_.end()) {
90 return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered");
91 }
92 hci::Address peer;
93 log::assert_that(
94 hci::Address::FromString(request->remote().address(), peer),
95 "assert failed: hci::Address::FromString(request->remote().address(), peer)");
96 dynamic_channel_helper_map_[request->psm()]->Connect(peer);
97 return ::grpc::Status::OK;
98 }
99
CloseChannel(::grpc::ServerContext *,const::bluetooth::l2cap::classic::CloseChannelRequest * request,::google::protobuf::Empty *)100 ::grpc::Status CloseChannel(
101 ::grpc::ServerContext* /* context */,
102 const ::bluetooth::l2cap::classic::CloseChannelRequest* request,
103 ::google::protobuf::Empty* /* response */) override {
104 auto psm = request->psm();
105 if (dynamic_channel_helper_map_.find(request->psm()) == dynamic_channel_helper_map_.end()) {
106 return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered");
107 }
108 dynamic_channel_helper_map_[psm]->Disconnect();
109 return ::grpc::Status::OK;
110 }
111
FetchL2capData(::grpc::ServerContext * context,const::google::protobuf::Empty *,::grpc::ServerWriter<classic::L2capPacket> * writer)112 ::grpc::Status FetchL2capData(
113 ::grpc::ServerContext* context,
114 const ::google::protobuf::Empty* /* request */,
115 ::grpc::ServerWriter<classic::L2capPacket>* writer) override {
116 auto status = pending_l2cap_data_.RunLoop(context, writer);
117
118 return status;
119 }
120
SetDynamicChannel(::grpc::ServerContext *,const SetEnableDynamicChannelRequest * request,google::protobuf::Empty *)121 ::grpc::Status SetDynamicChannel(
122 ::grpc::ServerContext* /* context */,
123 const SetEnableDynamicChannelRequest* request,
124 google::protobuf::Empty* /* response */) override {
125 dynamic_channel_helper_map_.emplace(
126 request->psm(), std::make_unique<L2capDynamicChannelHelper>(this, l2cap_layer_, facade_handler_, request->psm(),
127 request->retransmission_mode()));
128 return ::grpc::Status::OK;
129 }
130
SetTrafficPaused(::grpc::ServerContext *,const SetTrafficPausedRequest * request,::google::protobuf::Empty *)131 ::grpc::Status SetTrafficPaused(
132 ::grpc::ServerContext* /* context */,
133 const SetTrafficPausedRequest* request,
134 ::google::protobuf::Empty* /* response */) override {
135 auto psm = request->psm();
136 if (dynamic_channel_helper_map_.find(request->psm()) == dynamic_channel_helper_map_.end()) {
137 return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered");
138 }
139 if (request->paused()) {
140 dynamic_channel_helper_map_[psm]->SuspendDequeue();
141 } else {
142 dynamic_channel_helper_map_[psm]->ResumeDequeue();
143 }
144 return ::grpc::Status::OK;
145 }
146
GetChannelQueueDepth(::grpc::ServerContext *,const::google::protobuf::Empty *,GetChannelQueueDepthResponse * response)147 ::grpc::Status GetChannelQueueDepth(
148 ::grpc::ServerContext* /* context */,
149 const ::google::protobuf::Empty* /* request */,
150 GetChannelQueueDepthResponse* response) override {
151 // Use the value kChannelQueueSize (5) in internal/dynamic_channel_impl.h
152 response->set_size(5);
153 return ::grpc::Status::OK;
154 }
155
InitiateConnectionForSecurity(::grpc::ServerContext *,const blueberry::facade::BluetoothAddress * request,::google::protobuf::Empty *)156 ::grpc::Status InitiateConnectionForSecurity(
157 ::grpc::ServerContext* /* context */,
158 const blueberry::facade::BluetoothAddress* request,
159 ::google::protobuf::Empty* /* response */) override {
160 hci::Address peer;
161 log::assert_that(
162 hci::Address::FromString(request->address(), peer),
163 "assert failed: hci::Address::FromString(request->address(), peer)");
164 outgoing_pairing_remote_devices_.insert(peer);
165 security_interface_->InitiateConnectionForSecurity(peer);
166 return ::grpc::Status::OK;
167 }
168
SecurityConnectionEventOccurred(hci::ErrorCode,hci::Address remote,LinkSecurityInterfaceCallbackEventType event_type)169 void SecurityConnectionEventOccurred(
170 hci::ErrorCode /* hci_status */,
171 hci::Address remote,
172 LinkSecurityInterfaceCallbackEventType event_type) {
173 LinkSecurityInterfaceCallbackEvent msg;
174 msg.mutable_address()->set_address(remote.ToString());
175 msg.set_event_type(event_type);
176 security_connection_events_.OnIncomingEvent(msg);
177 }
178
FetchSecurityConnectionEvents(::grpc::ServerContext * context,const::google::protobuf::Empty *,::grpc::ServerWriter<LinkSecurityInterfaceCallbackEvent> * writer)179 ::grpc::Status FetchSecurityConnectionEvents(
180 ::grpc::ServerContext* context,
181 const ::google::protobuf::Empty* /* request */,
182 ::grpc::ServerWriter<LinkSecurityInterfaceCallbackEvent>* writer) override {
183 security_interface_ = l2cap_layer_->GetSecurityInterface(facade_handler_, this);
184 return security_connection_events_.RunLoop(context, writer);
185 }
186
SecurityLinkHold(::grpc::ServerContext *,const blueberry::facade::BluetoothAddress * request,::google::protobuf::Empty *)187 ::grpc::Status SecurityLinkHold(
188 ::grpc::ServerContext* /* context */,
189 const blueberry::facade::BluetoothAddress* request,
190 ::google::protobuf::Empty* /* response */) override {
191 hci::Address peer;
192 log::assert_that(
193 hci::Address::FromString(request->address(), peer),
194 "assert failed: hci::Address::FromString(request->address(), peer)");
195 auto entry = security_link_map_.find(peer);
196 if (entry == security_link_map_.end()) {
197 log::warn("Unknown address '{}'", peer);
198 } else {
199 entry->second->Hold();
200 }
201 return ::grpc::Status::OK;
202 }
203
SecurityLinkEnsureAuthenticated(::grpc::ServerContext *,const blueberry::facade::BluetoothAddress * request,::google::protobuf::Empty *)204 ::grpc::Status SecurityLinkEnsureAuthenticated(
205 ::grpc::ServerContext* /* context */,
206 const blueberry::facade::BluetoothAddress* request,
207 ::google::protobuf::Empty* /* response */) override {
208 hci::Address peer;
209 log::assert_that(
210 hci::Address::FromString(request->address(), peer),
211 "assert failed: hci::Address::FromString(request->address(), peer)");
212 auto entry = security_link_map_.find(peer);
213 if (entry == security_link_map_.end()) {
214 log::warn("Unknown address '{}'", peer);
215 } else {
216 entry->second->EnsureAuthenticated();
217 }
218 return ::grpc::Status::OK;
219 }
220
SecurityLinkRelease(::grpc::ServerContext *,const blueberry::facade::BluetoothAddress * request,::google::protobuf::Empty *)221 ::grpc::Status SecurityLinkRelease(
222 ::grpc::ServerContext* /* context */,
223 const blueberry::facade::BluetoothAddress* request,
224 ::google::protobuf::Empty* /* response */) override {
225 hci::Address peer;
226 log::assert_that(
227 hci::Address::FromString(request->address(), peer),
228 "assert failed: hci::Address::FromString(request->address(), peer)");
229 outgoing_pairing_remote_devices_.erase(peer);
230 auto entry = security_link_map_.find(peer);
231 if (entry == security_link_map_.end()) {
232 log::warn("Unknown address '{}'", peer);
233 } else {
234 entry->second->Release();
235 }
236 return ::grpc::Status::OK;
237 }
238
SecurityLinkDisconnect(::grpc::ServerContext *,const blueberry::facade::BluetoothAddress * request,::google::protobuf::Empty *)239 ::grpc::Status SecurityLinkDisconnect(
240 ::grpc::ServerContext* /* context */,
241 const blueberry::facade::BluetoothAddress* request,
242 ::google::protobuf::Empty* /* response */) override {
243 hci::Address peer;
244 log::assert_that(
245 hci::Address::FromString(request->address(), peer),
246 "assert failed: hci::Address::FromString(request->address(), peer)");
247 outgoing_pairing_remote_devices_.erase(peer);
248 auto entry = security_link_map_.find(peer);
249 if (entry == security_link_map_.end()) {
250 log::warn("Unknown address '{}'", peer);
251 } else {
252 entry->second->Disconnect();
253 }
254 return ::grpc::Status::OK;
255 }
256
OnLinkConnected(std::unique_ptr<LinkSecurityInterface> link)257 void OnLinkConnected(std::unique_ptr<LinkSecurityInterface> link) override {
258 auto remote = link->GetRemoteAddress();
259 if (outgoing_pairing_remote_devices_.count(remote) == 1) {
260 link->Hold();
261 link->EnsureAuthenticated();
262 outgoing_pairing_remote_devices_.erase(remote);
263 }
264 security_link_map_.emplace(remote, std::move(link));
265 SecurityConnectionEventOccurred(
266 hci::ErrorCode::SUCCESS, remote, LinkSecurityInterfaceCallbackEventType::ON_CONNECTED);
267 }
268
OnLinkDisconnected(hci::Address remote)269 void OnLinkDisconnected(hci::Address remote) override {
270 auto entry = security_link_map_.find(remote);
271 if (entry == security_link_map_.end()) {
272 log::warn("Unknown address '{}'", remote);
273 return;
274 }
275 entry->second.reset();
276 security_link_map_.erase(entry);
277 SecurityConnectionEventOccurred(
278 hci::ErrorCode::SUCCESS, remote, LinkSecurityInterfaceCallbackEventType::ON_DISCONNECTED);
279 }
280
OnAuthenticationComplete(hci::ErrorCode hci_status,hci::Address remote)281 void OnAuthenticationComplete(hci::ErrorCode hci_status, hci::Address remote) override {
282 auto entry = security_link_map_.find(remote);
283 if (entry != security_link_map_.end()) {
284 entry->second->EnsureEncrypted();
285 return;
286 }
287 SecurityConnectionEventOccurred(
288 hci_status, remote, LinkSecurityInterfaceCallbackEventType::ON_AUTHENTICATION_COMPLETE);
289 }
290
OnEncryptionChange(hci::Address remote,bool)291 void OnEncryptionChange(hci::Address remote, bool /* encrypted */) override {
292 SecurityConnectionEventOccurred(
293 hci::ErrorCode::SUCCESS, remote, LinkSecurityInterfaceCallbackEventType::ON_ENCRYPTION_CHANGE);
294 }
295
296 class L2capDynamicChannelHelper {
297 public:
L2capDynamicChannelHelper(L2capClassicModuleFacadeService * service,L2capClassicModule * l2cap_layer,os::Handler * handler,Psm psm,RetransmissionFlowControlMode mode)298 L2capDynamicChannelHelper(L2capClassicModuleFacadeService* service, L2capClassicModule* l2cap_layer,
299 os::Handler* handler, Psm psm, RetransmissionFlowControlMode mode)
300 : facade_service_(service), l2cap_layer_(l2cap_layer), handler_(handler), psm_(psm), mode_(mode) {
301 dynamic_channel_manager_ = l2cap_layer_->GetDynamicChannelManager();
302 DynamicChannelConfigurationOption configuration_option = {};
303 configuration_option.channel_mode = (DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode)mode;
304 dynamic_channel_manager_->RegisterService(
305 psm,
306 configuration_option,
307 SecurityPolicy::_SDP_ONLY_NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK,
308 handler_->BindOnceOn(this, &L2capDynamicChannelHelper::on_l2cap_service_registration_complete),
309 handler_->BindOn(this, &L2capDynamicChannelHelper::on_connection_open));
310 }
311
~L2capDynamicChannelHelper()312 ~L2capDynamicChannelHelper() {
313 if (dequeue_registered_) {
314 channel_->GetQueueUpEnd()->UnregisterDequeue();
315 channel_ = nullptr;
316 }
317 enqueue_buffer_.reset();
318 }
319
Connect(hci::Address address)320 void Connect(hci::Address address) {
321 DynamicChannelConfigurationOption configuration_option = l2cap::classic::DynamicChannelConfigurationOption();
322 configuration_option.channel_mode = (DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode)mode_;
323
324 dynamic_channel_manager_->ConnectChannel(
325 address,
326 configuration_option,
327 psm_,
328 handler_->BindOn(this, &L2capDynamicChannelHelper::on_connection_open),
329 handler_->BindOnceOn(this, &L2capDynamicChannelHelper::on_connect_fail));
330 std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
331 if (!channel_open_cv_.wait_for(lock, std::chrono::seconds(2), [this] { return channel_ != nullptr; })) {
332 log::warn("Channel is not open for psm {}", psm_);
333 }
334 }
335
Disconnect()336 void Disconnect() {
337 if (channel_ == nullptr) {
338 std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
339 if (!channel_open_cv_.wait_for(lock, std::chrono::seconds(2), [this] { return channel_ != nullptr; })) {
340 log::warn("Channel is not open for psm {}", psm_);
341 return;
342 }
343 }
344 channel_->Close();
345 }
346
on_l2cap_service_registration_complete(DynamicChannelManager::RegistrationResult,std::unique_ptr<DynamicChannelService>)347 void on_l2cap_service_registration_complete(
348 DynamicChannelManager::RegistrationResult /* registration_result */,
349 std::unique_ptr<DynamicChannelService> /* service */) {}
350
351 // invoked from Facade Handler
on_connection_open(std::unique_ptr<DynamicChannel> channel)352 void on_connection_open(std::unique_ptr<DynamicChannel> channel) {
353 ConnectionCompleteEvent event;
354 event.mutable_remote()->set_address(channel->GetDevice().GetAddress().ToString());
355 facade_service_->pending_connection_complete_.OnIncomingEvent(event);
356 {
357 std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
358 channel_ = std::move(channel);
359 enqueue_buffer_ = std::make_unique<os::EnqueueBuffer<BasePacketBuilder>>(channel_->GetQueueUpEnd());
360 }
361 channel_open_cv_.notify_all();
362 channel_->RegisterOnCloseCallback(
363 facade_service_->facade_handler_->BindOnceOn(this, &L2capDynamicChannelHelper::on_close_callback));
364 dequeue_registered_ = true;
365 channel_->GetQueueUpEnd()->RegisterDequeue(
366 facade_service_->facade_handler_,
367 common::Bind(&L2capDynamicChannelHelper::on_incoming_packet, common::Unretained(this)));
368 }
369
on_close_callback(hci::ErrorCode error_code)370 void on_close_callback(hci::ErrorCode error_code) {
371 {
372 std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
373 if (dequeue_registered_.exchange(false)) {
374 channel_->GetQueueUpEnd()->UnregisterDequeue();
375 }
376 }
377 classic::ConnectionCloseEvent event;
378 event.mutable_remote()->set_address(channel_->GetDevice().GetAddress().ToString());
379 event.set_reason(static_cast<uint32_t>(error_code));
380 facade_service_->pending_connection_close_.OnIncomingEvent(event);
381 channel_ = nullptr;
382 enqueue_buffer_.reset();
383 }
384
SuspendDequeue()385 void SuspendDequeue() {
386 if (dequeue_registered_.exchange(false)) {
387 channel_->GetQueueUpEnd()->UnregisterDequeue();
388 }
389 }
390
ResumeDequeue()391 void ResumeDequeue() {
392 if (!dequeue_registered_.exchange(true)) {
393 channel_->GetQueueUpEnd()->RegisterDequeue(
394 facade_service_->facade_handler_,
395 common::Bind(&L2capDynamicChannelHelper::on_incoming_packet, common::Unretained(this)));
396 }
397 }
398
on_connect_fail(DynamicChannelManager::ConnectionResult)399 void on_connect_fail(DynamicChannelManager::ConnectionResult /* result */) {}
400
on_incoming_packet()401 void on_incoming_packet() {
402 auto packet = channel_->GetQueueUpEnd()->TryDequeue();
403 std::string data = std::string(packet->begin(), packet->end());
404 L2capPacket l2cap_data;
405 l2cap_data.set_psm(psm_);
406 l2cap_data.set_payload(data);
407 facade_service_->pending_l2cap_data_.OnIncomingEvent(l2cap_data);
408 }
409
SendPacket(std::vector<uint8_t> packet)410 bool SendPacket(std::vector<uint8_t> packet) {
411 if (channel_ == nullptr) {
412 std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
413 if (!channel_open_cv_.wait_for(lock, std::chrono::seconds(2), [this] { return channel_ != nullptr; })) {
414 log::warn("Channel is not open");
415 return false;
416 }
417 }
418 auto packet_one = std::make_unique<packet::RawBuilder>(2000);
419 packet_one->AddOctets(packet);
420 enqueue_buffer_->Enqueue(std::move(packet_one), handler_);
421 return true;
422 }
423 L2capClassicModuleFacadeService* facade_service_;
424 L2capClassicModule* l2cap_layer_;
425 os::Handler* handler_;
426 std::unique_ptr<DynamicChannelManager> dynamic_channel_manager_;
427 std::unique_ptr<DynamicChannelService> service_;
428 std::unique_ptr<DynamicChannel> channel_ = nullptr;
429 std::unique_ptr<os::EnqueueBuffer<BasePacketBuilder>> enqueue_buffer_ = nullptr;
430 Psm psm_;
431 RetransmissionFlowControlMode mode_ = RetransmissionFlowControlMode::BASIC;
432 std::atomic_bool dequeue_registered_ = false;
433 std::condition_variable channel_open_cv_;
434 std::mutex channel_open_cv_mutex_;
435 };
436
437 L2capClassicModule* l2cap_layer_;
438 ::bluetooth::os::Handler* facade_handler_;
439 std::mutex channel_map_mutex_;
440 std::map<Psm, std::unique_ptr<L2capDynamicChannelHelper>> dynamic_channel_helper_map_;
441 ::bluetooth::grpc::GrpcEventQueue<classic::ConnectionCompleteEvent> pending_connection_complete_{
442 "FetchConnectionComplete"};
443 ::bluetooth::grpc::GrpcEventQueue<classic::ConnectionCloseEvent> pending_connection_close_{"FetchConnectionClose"};
444 ::bluetooth::grpc::GrpcEventQueue<L2capPacket> pending_l2cap_data_{"FetchL2capData"};
445 ::bluetooth::grpc::GrpcEventQueue<LinkSecurityInterfaceCallbackEvent> security_connection_events_{
446 "Security Connection Events"};
447 SecurityInterface* security_interface_;
448 std::unordered_map<hci::Address, std::unique_ptr<l2cap::classic::LinkSecurityInterface>> security_link_map_;
449 std::set<hci::Address> outgoing_pairing_remote_devices_;
450 };
451
ListDependencies(ModuleList * list) const452 void L2capClassicModuleFacadeModule::ListDependencies(ModuleList* list) const {
453 ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
454 list->add<l2cap::classic::L2capClassicModule>();
455 }
456
Start()457 void L2capClassicModuleFacadeModule::Start() {
458 ::bluetooth::grpc::GrpcFacadeModule::Start();
459 service_ = new L2capClassicModuleFacadeService(GetDependency<l2cap::classic::L2capClassicModule>(), GetHandler());
460 }
461
Stop()462 void L2capClassicModuleFacadeModule::Stop() {
463 delete service_;
464 ::bluetooth::grpc::GrpcFacadeModule::Stop();
465 }
466
GetService() const467 ::grpc::Service* L2capClassicModuleFacadeModule::GetService() const {
468 return service_;
469 }
470
471 const ModuleFactory L2capClassicModuleFacadeModule::Factory =
__anonbd297ca20402() 472 ::bluetooth::ModuleFactory([]() { return new L2capClassicModuleFacadeModule(); });
473
474 } // namespace classic
475 } // namespace l2cap
476 } // namespace bluetooth
477