1 /*
2  * Copyright 2020 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/le/facade.h"
18 
19 #include "grpc/grpc_event_queue.h"
20 #include "l2cap/le/dynamic_channel.h"
21 #include "l2cap/le/dynamic_channel_manager.h"
22 #include "l2cap/le/dynamic_channel_service.h"
23 #include "l2cap/le/facade.grpc.pb.h"
24 #include "l2cap/le/l2cap_le_module.h"
25 #include "l2cap/le/security_policy.h"
26 #include "l2cap/psm.h"
27 #include "packet/raw_builder.h"
28 
29 namespace bluetooth {
30 namespace l2cap {
31 namespace le {
32 
SecurityLevelToPolicy(SecurityLevel level)33 SecurityPolicy SecurityLevelToPolicy(SecurityLevel level) {
34   switch (level) {
35     case SecurityLevel::NO_SECURITY:
36       return SecurityPolicy::NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK;
37     case SecurityLevel::UNAUTHENTICATED_PAIRING_WITH_ENCRYPTION:
38       return SecurityPolicy::ENCRYPTED_TRANSPORT;
39     case SecurityLevel::AUTHENTICATED_PAIRING_WITH_ENCRYPTION:
40       return SecurityPolicy::AUTHENTICATED_ENCRYPTED_TRANSPORT;
41     case SecurityLevel::AUTHENTICATED_PAIRING_WITH_128_BIT_KEY:
42       return SecurityPolicy::_NOT_FOR_YOU__AUTHENTICATED_PAIRING_WITH_128_BIT_KEY;
43     case SecurityLevel::AUTHORIZATION:
44       return SecurityPolicy::_NOT_FOR_YOU__AUTHORIZATION;
45     default:
46       return SecurityPolicy::NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK;
47   }
48 }
49 
50 static constexpr auto kChannelOpenTimeout = std::chrono::seconds(4);
51 
52 class L2capLeModuleFacadeService : public L2capLeModuleFacade::Service {
53  public:
L2capLeModuleFacadeService(L2capLeModule * l2cap_layer,os::Handler * facade_handler)54   L2capLeModuleFacadeService(L2capLeModule* l2cap_layer, os::Handler* facade_handler)
55       : l2cap_layer_(l2cap_layer), facade_handler_(facade_handler) {
56     ASSERT(l2cap_layer_ != nullptr);
57     ASSERT(facade_handler_ != nullptr);
58   }
59 
FetchL2capData(::grpc::ServerContext * context,const::google::protobuf::Empty * request,::grpc::ServerWriter<::bluetooth::l2cap::le::L2capPacket> * writer)60   ::grpc::Status FetchL2capData(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
61                                 ::grpc::ServerWriter<::bluetooth::l2cap::le::L2capPacket>* writer) override {
62     return pending_l2cap_data_.RunLoop(context, writer);
63   }
64 
OpenDynamicChannel(::grpc::ServerContext * context,const OpenDynamicChannelRequest * request,OpenDynamicChannelResponse * response)65   ::grpc::Status OpenDynamicChannel(::grpc::ServerContext* context, const OpenDynamicChannelRequest* request,
66                                     OpenDynamicChannelResponse* response) override {
67     auto service_helper = dynamic_channel_helper_map_.find(request->psm());
68     if (service_helper == dynamic_channel_helper_map_.end()) {
69       return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered");
70     }
71     hci::Address peer_address;
72     ASSERT(hci::Address::FromString(request->remote().address().address(), peer_address));
73     // TODO: Support different address type
74     hci::AddressWithType peer(peer_address, hci::AddressType::RANDOM_DEVICE_ADDRESS);
75     service_helper->second->Connect(peer);
76     response->set_status(
77         static_cast<int>(service_helper->second->channel_open_fail_reason_.l2cap_connection_response_result));
78     return ::grpc::Status::OK;
79   }
80 
CloseDynamicChannel(::grpc::ServerContext * context,const CloseDynamicChannelRequest * request,::google::protobuf::Empty * response)81   ::grpc::Status CloseDynamicChannel(::grpc::ServerContext* context, const CloseDynamicChannelRequest* request,
82                                      ::google::protobuf::Empty* response) override {
83     auto service_helper = dynamic_channel_helper_map_.find(request->psm());
84     if (service_helper == dynamic_channel_helper_map_.end()) {
85       return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered");
86     }
87     if (service_helper->second->channel_ == nullptr) {
88       return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Channel not open");
89     }
90     auto address = service_helper->second->channel_->GetDevice().GetAddress();
91     hci::Address peer_address;
92     ASSERT(hci::Address::FromString(request->remote().address().address(), peer_address));
93     if (address != peer_address) {
94       return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Remote address doesn't match");
95     }
96     service_helper->second->channel_->Close();
97     return ::grpc::Status::OK;
98   }
99 
SetDynamicChannel(::grpc::ServerContext * context,const::bluetooth::l2cap::le::SetEnableDynamicChannelRequest * request,::google::protobuf::Empty * response)100   ::grpc::Status SetDynamicChannel(::grpc::ServerContext* context,
101                                    const ::bluetooth::l2cap::le::SetEnableDynamicChannelRequest* request,
102                                    ::google::protobuf::Empty* response) override {
103     if (request->enable()) {
104       dynamic_channel_helper_map_.emplace(request->psm(), std::make_unique<L2capDynamicChannelHelper>(
105                                                               this, l2cap_layer_, facade_handler_, request->psm(),
106                                                               SecurityLevelToPolicy(request->security_level())));
107       return ::grpc::Status::OK;
108     } else {
109       auto service_helper = dynamic_channel_helper_map_.find(request->psm());
110       if (service_helper == dynamic_channel_helper_map_.end()) {
111         return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered");
112       }
113       service_helper->second->service_->Unregister(common::BindOnce([] {}), facade_handler_);
114       return ::grpc::Status::OK;
115     }
116   }
117 
SendDynamicChannelPacket(::grpc::ServerContext * context,const::bluetooth::l2cap::le::DynamicChannelPacket * request,::google::protobuf::Empty * response)118   ::grpc::Status SendDynamicChannelPacket(::grpc::ServerContext* context,
119                                           const ::bluetooth::l2cap::le::DynamicChannelPacket* request,
120                                           ::google::protobuf::Empty* response) override {
121     std::unique_lock<std::mutex> lock(channel_map_mutex_);
122     if (dynamic_channel_helper_map_.find(request->psm()) == dynamic_channel_helper_map_.end()) {
123       return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered");
124     }
125     std::vector<uint8_t> packet(request->payload().begin(), request->payload().end());
126     if (!dynamic_channel_helper_map_[request->psm()]->SendPacket(packet)) {
127       return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Channel not open");
128     }
129     return ::grpc::Status::OK;
130   }
131 
132   class L2capDynamicChannelHelper {
133    public:
L2capDynamicChannelHelper(L2capLeModuleFacadeService * service,L2capLeModule * l2cap_layer,os::Handler * handler,Psm psm,SecurityPolicy security_policy)134     L2capDynamicChannelHelper(L2capLeModuleFacadeService* service, L2capLeModule* l2cap_layer, os::Handler* handler,
135                               Psm psm, SecurityPolicy security_policy)
136         : facade_service_(service), l2cap_layer_(l2cap_layer), handler_(handler), psm_(psm) {
137       dynamic_channel_manager_ = l2cap_layer_->GetDynamicChannelManager();
138       dynamic_channel_manager_->RegisterService(
139           psm, {}, security_policy,
140           common::BindOnce(&L2capDynamicChannelHelper::on_l2cap_service_registration_complete,
141                            common::Unretained(this)),
142           common::Bind(&L2capDynamicChannelHelper::on_connection_open, common::Unretained(this)), handler_);
143     }
144 
~L2capDynamicChannelHelper()145     ~L2capDynamicChannelHelper() {
146       if (channel_ != nullptr) {
147         channel_->GetQueueUpEnd()->UnregisterDequeue();
148         channel_ = nullptr;
149       }
150     }
151 
Connect(hci::AddressWithType address)152     void Connect(hci::AddressWithType address) {
153       dynamic_channel_manager_->ConnectChannel(
154           address, {}, psm_, common::Bind(&L2capDynamicChannelHelper::on_connection_open, common::Unretained(this)),
155           common::Bind(&L2capDynamicChannelHelper::on_connect_fail, common::Unretained(this)), handler_);
156       std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
157       if (!channel_open_cv_.wait_for(lock, kChannelOpenTimeout, [this] { return channel_ != nullptr; })) {
158         LOG_WARN("Channel is not open for psm %d", psm_);
159       }
160     }
161 
on_l2cap_service_registration_complete(DynamicChannelManager::RegistrationResult registration_result,std::unique_ptr<DynamicChannelService> service)162     void on_l2cap_service_registration_complete(DynamicChannelManager::RegistrationResult registration_result,
163                                                 std::unique_ptr<DynamicChannelService> service) {
164       if (registration_result != DynamicChannelManager::RegistrationResult::SUCCESS) {
165         LOG_ERROR("Service registration failed");
166       } else {
167         service_ = std::move(service);
168       }
169     }
170 
171     // invoked from Facade Handler
on_connection_open(std::unique_ptr<DynamicChannel> channel)172     void on_connection_open(std::unique_ptr<DynamicChannel> channel) {
173       {
174         std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
175         channel_ = std::move(channel);
176       }
177       channel_open_cv_.notify_all();
178       channel_->RegisterOnCloseCallback(
179           facade_service_->facade_handler_->BindOnceOn(this, &L2capDynamicChannelHelper::on_close_callback));
180       channel_->GetQueueUpEnd()->RegisterDequeue(
181           facade_service_->facade_handler_,
182           common::Bind(&L2capDynamicChannelHelper::on_incoming_packet, common::Unretained(this)));
183     }
184 
on_close_callback(hci::ErrorCode error_code)185     void on_close_callback(hci::ErrorCode error_code) {
186       {
187         std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
188         channel_->GetQueueUpEnd()->UnregisterDequeue();
189       }
190       channel_ = nullptr;
191     }
192 
on_connect_fail(DynamicChannelManager::ConnectionResult result)193     void on_connect_fail(DynamicChannelManager::ConnectionResult result) {
194       {
195         std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
196         channel_ = nullptr;
197         channel_open_fail_reason_ = result;
198       }
199       channel_open_cv_.notify_all();
200     }
201 
on_incoming_packet()202     void on_incoming_packet() {
203       auto packet = channel_->GetQueueUpEnd()->TryDequeue();
204       std::string data = std::string(packet->begin(), packet->end());
205       L2capPacket l2cap_data;
206       l2cap_data.set_psm(psm_);
207       l2cap_data.set_payload(data);
208       facade_service_->pending_l2cap_data_.OnIncomingEvent(l2cap_data);
209     }
210 
SendPacket(std::vector<uint8_t> packet)211     bool SendPacket(std::vector<uint8_t> packet) {
212       if (channel_ == nullptr) {
213         std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
214         if (!channel_open_cv_.wait_for(lock, kChannelOpenTimeout, [this] { return channel_ != nullptr; })) {
215           LOG_WARN("Channel is not open for psm %d", psm_);
216           return false;
217         }
218       }
219       std::promise<void> promise;
220       auto future = promise.get_future();
221       channel_->GetQueueUpEnd()->RegisterEnqueue(
222           handler_, common::Bind(&L2capDynamicChannelHelper::enqueue_callback, common::Unretained(this), packet,
223                                  common::Passed(std::move(promise))));
224       auto status = future.wait_for(std::chrono::milliseconds(500));
225       if (status != std::future_status::ready) {
226         LOG_ERROR("Can't send packet because the previous packet wasn't sent yet");
227         return false;
228       }
229       return true;
230     }
231 
enqueue_callback(std::vector<uint8_t> packet,std::promise<void> promise)232     std::unique_ptr<packet::BasePacketBuilder> enqueue_callback(std::vector<uint8_t> packet,
233                                                                 std::promise<void> promise) {
234       auto packet_one = std::make_unique<packet::RawBuilder>(2000);
235       packet_one->AddOctets(packet);
236       channel_->GetQueueUpEnd()->UnregisterEnqueue();
237       promise.set_value();
238       return packet_one;
239     }
240 
241     L2capLeModuleFacadeService* facade_service_;
242     L2capLeModule* l2cap_layer_;
243     os::Handler* handler_;
244     std::unique_ptr<DynamicChannelManager> dynamic_channel_manager_;
245     std::unique_ptr<DynamicChannelService> service_;
246     std::unique_ptr<DynamicChannel> channel_ = nullptr;
247     Psm psm_;
248     DynamicChannelManager::ConnectionResult channel_open_fail_reason_;
249     std::condition_variable channel_open_cv_;
250     std::mutex channel_open_cv_mutex_;
251   };
252 
SetFixedChannel(::grpc::ServerContext * context,const SetEnableFixedChannelRequest * request,::google::protobuf::Empty * response)253   ::grpc::Status SetFixedChannel(::grpc::ServerContext* context, const SetEnableFixedChannelRequest* request,
254                                  ::google::protobuf::Empty* response) override {
255     if (request->enable()) {
256       fixed_channel_helper_map_.emplace(request->cid(), std::make_unique<L2capFixedChannelHelper>(
257                                                             this, l2cap_layer_, facade_handler_, request->cid()));
258       return ::grpc::Status::OK;
259     } else {
260       auto service_helper = fixed_channel_helper_map_.find(request->cid());
261       if (service_helper == fixed_channel_helper_map_.end()) {
262         return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Cid not registered");
263       }
264       service_helper->second->channel_->Release();
265       service_helper->second->service_->Unregister(common::BindOnce([] {}), facade_handler_);
266       return ::grpc::Status::OK;
267     }
268   }
269 
SendFixedChannelPacket(::grpc::ServerContext * context,const FixedChannelPacket * request,::google::protobuf::Empty * response)270   ::grpc::Status SendFixedChannelPacket(::grpc::ServerContext* context, const FixedChannelPacket* request,
271                                         ::google::protobuf::Empty* response) override {
272     std::unique_lock<std::mutex> lock(channel_map_mutex_);
273     if (fixed_channel_helper_map_.find(request->cid()) == fixed_channel_helper_map_.end()) {
274       return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Cid not registered");
275     }
276     std::vector<uint8_t> packet(request->payload().begin(), request->payload().end());
277     if (!fixed_channel_helper_map_[request->cid()]->SendPacket(packet)) {
278       return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Channel not open");
279     }
280     return ::grpc::Status::OK;
281   }
282 
283   class L2capFixedChannelHelper {
284    public:
L2capFixedChannelHelper(L2capLeModuleFacadeService * service,L2capLeModule * l2cap_layer,os::Handler * handler,Cid cid)285     L2capFixedChannelHelper(L2capLeModuleFacadeService* service, L2capLeModule* l2cap_layer, os::Handler* handler,
286                             Cid cid)
287         : facade_service_(service), l2cap_layer_(l2cap_layer), handler_(handler), cid_(cid) {
288       fixed_channel_manager_ = l2cap_layer_->GetFixedChannelManager();
289       fixed_channel_manager_->RegisterService(
290           cid_,
291           common::BindOnce(&L2capFixedChannelHelper::on_l2cap_service_registration_complete, common::Unretained(this)),
292           common::Bind(&L2capFixedChannelHelper::on_connection_open, common::Unretained(this)), handler_);
293     }
294 
~L2capFixedChannelHelper()295     ~L2capFixedChannelHelper() {
296       if (channel_ != nullptr) {
297         channel_->GetQueueUpEnd()->UnregisterDequeue();
298         channel_->Release();
299         channel_ = nullptr;
300       }
301     }
302 
Connect(hci::AddressWithType address)303     void Connect(hci::AddressWithType address) {
304       fixed_channel_manager_->ConnectServices(
305           address, common::BindOnce(&L2capFixedChannelHelper::on_connect_fail, common::Unretained(this)), handler_);
306       std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
307       if (!channel_open_cv_.wait_for(lock, kChannelOpenTimeout, [this] { return channel_ != nullptr; })) {
308         LOG_WARN("Channel is not open for cid %d", cid_);
309       }
310     }
311 
on_l2cap_service_registration_complete(FixedChannelManager::RegistrationResult registration_result,std::unique_ptr<FixedChannelService> service)312     void on_l2cap_service_registration_complete(FixedChannelManager::RegistrationResult registration_result,
313                                                 std::unique_ptr<FixedChannelService> service) {
314       if (registration_result != FixedChannelManager::RegistrationResult::SUCCESS) {
315         LOG_ERROR("Service registration failed");
316       } else {
317         service_ = std::move(service);
318       }
319     }
320 
321     // invoked from Facade Handler
on_connection_open(std::unique_ptr<FixedChannel> channel)322     void on_connection_open(std::unique_ptr<FixedChannel> channel) {
323       {
324         std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
325         channel_ = std::move(channel);
326         channel_->RegisterOnCloseCallback(
327             handler_, common::BindOnce(&L2capFixedChannelHelper::on_close_callback, common::Unretained(this)));
328         channel_->Acquire();
329       }
330       channel_open_cv_.notify_all();
331       channel_->GetQueueUpEnd()->RegisterDequeue(
332           facade_service_->facade_handler_,
333           common::Bind(&L2capFixedChannelHelper::on_incoming_packet, common::Unretained(this)));
334     }
335 
on_close_callback(hci::ErrorCode error_code)336     void on_close_callback(hci::ErrorCode error_code) {
337       {
338         std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
339         channel_->GetQueueUpEnd()->UnregisterDequeue();
340       }
341       channel_ = nullptr;
342     }
343 
on_connect_fail(FixedChannelManager::ConnectionResult result)344     void on_connect_fail(FixedChannelManager::ConnectionResult result) {
345       {
346         std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
347         channel_ = nullptr;
348       }
349       channel_open_cv_.notify_all();
350     }
351 
on_incoming_packet()352     void on_incoming_packet() {
353       auto packet = channel_->GetQueueUpEnd()->TryDequeue();
354       std::string data = std::string(packet->begin(), packet->end());
355       L2capPacket l2cap_data;
356       l2cap_data.set_fixed_cid(cid_);
357       l2cap_data.set_payload(data);
358       facade_service_->pending_l2cap_data_.OnIncomingEvent(l2cap_data);
359     }
360 
SendPacket(std::vector<uint8_t> packet)361     bool SendPacket(std::vector<uint8_t> packet) {
362       if (channel_ == nullptr) {
363         std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
364         if (!channel_open_cv_.wait_for(lock, kChannelOpenTimeout, [this] { return channel_ != nullptr; })) {
365           LOG_WARN("Channel is not open for cid %d", cid_);
366           return false;
367         }
368       }
369       std::promise<void> promise;
370       auto future = promise.get_future();
371       channel_->GetQueueUpEnd()->RegisterEnqueue(
372           handler_, common::Bind(&L2capFixedChannelHelper::enqueue_callback, common::Unretained(this), packet,
373                                  common::Passed(std::move(promise))));
374       auto status = future.wait_for(std::chrono::milliseconds(500));
375       if (status != std::future_status::ready) {
376         LOG_ERROR("Can't send packet because the previous packet wasn't sent yet");
377         return false;
378       }
379       return true;
380     }
381 
enqueue_callback(std::vector<uint8_t> packet,std::promise<void> promise)382     std::unique_ptr<packet::BasePacketBuilder> enqueue_callback(std::vector<uint8_t> packet,
383                                                                 std::promise<void> promise) {
384       auto packet_one = std::make_unique<packet::RawBuilder>(2000);
385       packet_one->AddOctets(packet);
386       channel_->GetQueueUpEnd()->UnregisterEnqueue();
387       promise.set_value();
388       return packet_one;
389     }
390 
391     L2capLeModuleFacadeService* facade_service_;
392     L2capLeModule* l2cap_layer_;
393     os::Handler* handler_;
394     std::unique_ptr<FixedChannelManager> fixed_channel_manager_;
395     std::unique_ptr<FixedChannelService> service_;
396     std::unique_ptr<FixedChannel> channel_ = nullptr;
397     Cid cid_;
398     std::condition_variable channel_open_cv_;
399     std::mutex channel_open_cv_mutex_;
400   };
401 
SendConnectionParameterUpdate(::grpc::ServerContext * context,const ConnectionParameter * request,::google::protobuf::Empty * response)402   ::grpc::Status SendConnectionParameterUpdate(::grpc::ServerContext* context, const ConnectionParameter* request,
403                                                ::google::protobuf::Empty* response) override {
404     if (dynamic_channel_helper_map_.empty()) {
405       return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Need to open at least one dynamic channel first");
406     }
407     auto& dynamic_channel_helper = dynamic_channel_helper_map_.begin()->second;
408     dynamic_channel_helper->channel_->GetLinkOptions()->UpdateConnectionParameter(
409         request->conn_interval_min(), request->conn_interval_max(), request->conn_latency(),
410         request->supervision_timeout(), request->min_ce_length(), request->max_ce_length());
411 
412     return ::grpc::Status::OK;
413   }
414 
415   L2capLeModule* l2cap_layer_;
416   os::Handler* facade_handler_;
417   std::mutex channel_map_mutex_;
418   std::map<Psm, std::unique_ptr<L2capDynamicChannelHelper>> dynamic_channel_helper_map_;
419   std::map<Cid, std::unique_ptr<L2capFixedChannelHelper>> fixed_channel_helper_map_;
420   ::bluetooth::grpc::GrpcEventQueue<L2capPacket> pending_l2cap_data_{"FetchL2capData"};
421 };
422 
ListDependencies(ModuleList * list)423 void L2capLeModuleFacadeModule::ListDependencies(ModuleList* list) {
424   ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
425   list->add<l2cap::le::L2capLeModule>();
426 }
427 
Start()428 void L2capLeModuleFacadeModule::Start() {
429   ::bluetooth::grpc::GrpcFacadeModule::Start();
430   service_ = new L2capLeModuleFacadeService(GetDependency<l2cap::le::L2capLeModule>(), GetHandler());
431 }
432 
Stop()433 void L2capLeModuleFacadeModule::Stop() {
434   delete service_;
435   ::bluetooth::grpc::GrpcFacadeModule::Stop();
436 }
437 
GetService() const438 ::grpc::Service* L2capLeModuleFacadeModule::GetService() const {
439   return service_;
440 }
441 
442 const ModuleFactory L2capLeModuleFacadeModule::Factory =
__anonc78efb070702() 443     ::bluetooth::ModuleFactory([]() { return new L2capLeModuleFacadeModule(); });
444 
445 }  // namespace le
446 }  // namespace l2cap
447 }  // namespace bluetooth
448