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 #define LOG_TAG "bt_gd_neigh"
17
18 #include "neighbor/inquiry.h"
19
20 #include <memory>
21
22 #include "common/bind.h"
23 #include "hci/hci_layer.h"
24 #include "hci/hci_packets.h"
25 #include "module.h"
26 #include "os/handler.h"
27 #include "os/log.h"
28
29 namespace bluetooth {
30 namespace neighbor {
31
32 static constexpr uint8_t kGeneralInquiryAccessCode = 0x33;
33 static constexpr uint8_t kLimitedInquiryAccessCode = 0x00;
34
35 struct InquiryModule::impl {
36 void RegisterCallbacks(InquiryCallbacks inquiry_callbacks);
37 void UnregisterCallbacks();
38
39 void StartOneShotInquiry(bool limited, InquiryLength inquiry_length, NumResponses num_responses);
40 void StopOneShotInquiry();
41
42 void StartPeriodicInquiry(
43 bool limited,
44 InquiryLength inquiry_length,
45 NumResponses num_responses,
46 PeriodLength max_delay,
47 PeriodLength min_delay);
48 void StopPeriodicInquiry();
49
50 void SetScanActivity(ScanParameters params);
51
52 void SetScanType(hci::InquiryScanType scan_type);
53
54 void SetInquiryMode(hci::InquiryMode mode);
55
56 void Start();
57 void Stop();
58
59 bool HasCallbacks() const;
60
61 impl(InquiryModule& inquiry_module);
62
63 private:
64 InquiryCallbacks inquiry_callbacks_;
65
66 InquiryModule& module_;
67
68 bool active_general_one_shot_{false};
69 bool active_limited_one_shot_{false};
70 bool active_general_periodic_{false};
71 bool active_limited_periodic_{false};
72
73 ScanParameters inquiry_scan_;
74 hci::InquiryMode inquiry_mode_;
75 hci::InquiryScanType inquiry_scan_type_;
76 int8_t inquiry_response_tx_power_;
77
78 bool IsInquiryActive() const;
79
80 void EnqueueCommandComplete(std::unique_ptr<hci::CommandPacketBuilder> command);
81 void EnqueueCommandStatus(std::unique_ptr<hci::CommandPacketBuilder> command);
82 void OnCommandComplete(hci::CommandCompleteView view);
83 void OnCommandStatus(hci::CommandStatusView status);
84
85 void EnqueueCommandCompleteSync(std::unique_ptr<hci::CommandPacketBuilder> command);
86 void OnCommandCompleteSync(hci::CommandCompleteView view);
87
88 void OnEvent(hci::EventPacketView view);
89
90 std::promise<void>* command_sync_{nullptr};
91
92 hci::HciLayer* hci_layer_;
93 os::Handler* handler_;
94 };
95
__anon5363d6b40102() 96 const ModuleFactory neighbor::InquiryModule::Factory = ModuleFactory([]() { return new neighbor::InquiryModule(); });
97
impl(neighbor::InquiryModule & module)98 neighbor::InquiryModule::impl::impl(neighbor::InquiryModule& module) : module_(module) {}
99
OnCommandCompleteSync(hci::CommandCompleteView view)100 void neighbor::InquiryModule::impl::OnCommandCompleteSync(hci::CommandCompleteView view) {
101 OnCommandComplete(view);
102 ASSERT(command_sync_ != nullptr);
103 command_sync_->set_value();
104 }
105
OnCommandComplete(hci::CommandCompleteView view)106 void neighbor::InquiryModule::impl::OnCommandComplete(hci::CommandCompleteView view) {
107 switch (view.GetCommandOpCode()) {
108 case hci::OpCode::INQUIRY_CANCEL: {
109 auto packet = hci::InquiryCancelCompleteView::Create(view);
110 ASSERT(packet.IsValid());
111 ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
112 } break;
113
114 case hci::OpCode::PERIODIC_INQUIRY_MODE: {
115 auto packet = hci::PeriodicInquiryModeCompleteView::Create(view);
116 ASSERT(packet.IsValid());
117 ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
118 } break;
119
120 case hci::OpCode::EXIT_PERIODIC_INQUIRY_MODE: {
121 auto packet = hci::ExitPeriodicInquiryModeCompleteView::Create(view);
122 ASSERT(packet.IsValid());
123 ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
124 } break;
125
126 case hci::OpCode::WRITE_INQUIRY_MODE: {
127 auto packet = hci::WriteInquiryModeCompleteView::Create(view);
128 ASSERT(packet.IsValid());
129 ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
130 } break;
131
132 case hci::OpCode::READ_INQUIRY_MODE: {
133 auto packet = hci::ReadInquiryModeCompleteView::Create(view);
134 ASSERT(packet.IsValid());
135 ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
136 inquiry_mode_ = packet.GetInquiryMode();
137 } break;
138
139 case hci::OpCode::READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL: {
140 auto packet = hci::ReadInquiryResponseTransmitPowerLevelCompleteView::Create(view);
141 ASSERT(packet.IsValid());
142 ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
143 inquiry_response_tx_power_ = packet.GetTxPower();
144 } break;
145
146 case hci::OpCode::WRITE_INQUIRY_SCAN_ACTIVITY: {
147 auto packet = hci::WriteInquiryScanActivityCompleteView::Create(view);
148 ASSERT(packet.IsValid());
149 ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
150 } break;
151
152 case hci::OpCode::READ_INQUIRY_SCAN_ACTIVITY: {
153 auto packet = hci::ReadInquiryScanActivityCompleteView::Create(view);
154 ASSERT(packet.IsValid());
155 ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
156 inquiry_scan_.interval = packet.GetInquiryScanInterval();
157 inquiry_scan_.window = packet.GetInquiryScanWindow();
158 } break;
159
160 case hci::OpCode::WRITE_INQUIRY_SCAN_TYPE: {
161 auto packet = hci::WriteInquiryScanTypeCompleteView::Create(view);
162 ASSERT(packet.IsValid());
163 ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
164 } break;
165
166 case hci::OpCode::READ_INQUIRY_SCAN_TYPE: {
167 auto packet = hci::ReadInquiryScanTypeCompleteView::Create(view);
168 ASSERT(packet.IsValid());
169 ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
170 inquiry_scan_type_ = packet.GetInquiryScanType();
171 } break;
172
173 default:
174 LOG_WARN("Unhandled command:%s", hci::OpCodeText(view.GetCommandOpCode()).c_str());
175 break;
176 }
177 }
178
OnCommandStatus(hci::CommandStatusView status)179 void neighbor::InquiryModule::impl::OnCommandStatus(hci::CommandStatusView status) {
180 ASSERT(status.GetStatus() == hci::ErrorCode::SUCCESS);
181
182 switch (status.GetCommandOpCode()) {
183 case hci::OpCode::INQUIRY: {
184 auto packet = hci::InquiryStatusView::Create(status);
185 ASSERT(packet.IsValid());
186 if (active_limited_one_shot_ || active_general_one_shot_) {
187 LOG_DEBUG("Inquiry started lap: %s", active_limited_one_shot_ ? "Limited" : "General");
188 }
189 } break;
190
191 default:
192 LOG_WARN("Unhandled command:%s", hci::OpCodeText(status.GetCommandOpCode()).c_str());
193 break;
194 }
195 }
196
OnEvent(hci::EventPacketView view)197 void neighbor::InquiryModule::impl::OnEvent(hci::EventPacketView view) {
198 switch (view.GetEventCode()) {
199 case hci::EventCode::INQUIRY_COMPLETE: {
200 auto packet = hci::InquiryCompleteView::Create(view);
201 ASSERT(packet.IsValid());
202 LOG_DEBUG("inquiry complete");
203 active_limited_one_shot_ = false;
204 active_general_one_shot_ = false;
205 inquiry_callbacks_.complete(packet.GetStatus());
206 } break;
207
208 case hci::EventCode::INQUIRY_RESULT: {
209 auto packet = hci::InquiryResultView::Create(view);
210 ASSERT(packet.IsValid());
211 LOG_DEBUG("Inquiry result size:%zd num_responses:%zu", packet.size(), packet.GetInquiryResults().size());
212 inquiry_callbacks_.result(packet);
213 } break;
214
215 case hci::EventCode::INQUIRY_RESULT_WITH_RSSI: {
216 auto packet = hci::InquiryResultWithRssiView::Create(view);
217 ASSERT(packet.IsValid());
218 LOG_DEBUG("Inquiry result with rssi num_responses:%zu", packet.GetInquiryResults().size());
219 inquiry_callbacks_.result_with_rssi(packet);
220 } break;
221
222 case hci::EventCode::EXTENDED_INQUIRY_RESULT: {
223 auto packet = hci::ExtendedInquiryResultView::Create(view);
224 ASSERT(packet.IsValid());
225 LOG_DEBUG(
226 "Extended inquiry result addr:%s repetition_mode:%s cod:%s clock_offset:%d rssi:%hhd",
227 packet.GetAddress().ToString().c_str(),
228 hci::PageScanRepetitionModeText(packet.GetPageScanRepetitionMode()).c_str(),
229 packet.GetClassOfDevice().ToString().c_str(),
230 packet.GetClockOffset(),
231 packet.GetRssi());
232 inquiry_callbacks_.extended_result(packet);
233 } break;
234
235 default:
236 LOG_ERROR("Unhandled event:%s", hci::EventCodeText(view.GetEventCode()).c_str());
237 break;
238 }
239 }
240
241 /**
242 * impl
243 */
RegisterCallbacks(InquiryCallbacks callbacks)244 void neighbor::InquiryModule::impl::RegisterCallbacks(InquiryCallbacks callbacks) {
245 inquiry_callbacks_ = callbacks;
246
247 hci_layer_->RegisterEventHandler(
248 hci::EventCode::INQUIRY_RESULT, handler_->BindOn(this, &InquiryModule::impl::OnEvent));
249 hci_layer_->RegisterEventHandler(
250 hci::EventCode::INQUIRY_RESULT_WITH_RSSI, handler_->BindOn(this, &InquiryModule::impl::OnEvent));
251 hci_layer_->RegisterEventHandler(
252 hci::EventCode::EXTENDED_INQUIRY_RESULT, handler_->BindOn(this, &InquiryModule::impl::OnEvent));
253 hci_layer_->RegisterEventHandler(
254 hci::EventCode::INQUIRY_COMPLETE, handler_->BindOn(this, &InquiryModule::impl::OnEvent));
255 }
256
UnregisterCallbacks()257 void neighbor::InquiryModule::impl::UnregisterCallbacks() {
258 hci_layer_->UnregisterEventHandler(hci::EventCode::INQUIRY_COMPLETE);
259 hci_layer_->UnregisterEventHandler(hci::EventCode::EXTENDED_INQUIRY_RESULT);
260 hci_layer_->UnregisterEventHandler(hci::EventCode::INQUIRY_RESULT_WITH_RSSI);
261 hci_layer_->UnregisterEventHandler(hci::EventCode::INQUIRY_RESULT);
262
263 inquiry_callbacks_ = {nullptr, nullptr, nullptr, nullptr};
264 }
265
EnqueueCommandComplete(std::unique_ptr<hci::CommandPacketBuilder> command)266 void neighbor::InquiryModule::impl::EnqueueCommandComplete(std::unique_ptr<hci::CommandPacketBuilder> command) {
267 hci_layer_->EnqueueCommand(std::move(command), handler_->BindOnceOn(this, &impl::OnCommandComplete));
268 }
269
EnqueueCommandStatus(std::unique_ptr<hci::CommandPacketBuilder> command)270 void neighbor::InquiryModule::impl::EnqueueCommandStatus(std::unique_ptr<hci::CommandPacketBuilder> command) {
271 hci_layer_->EnqueueCommand(std::move(command), handler_->BindOnceOn(this, &impl::OnCommandStatus));
272 }
273
EnqueueCommandCompleteSync(std::unique_ptr<hci::CommandPacketBuilder> command)274 void neighbor::InquiryModule::impl::EnqueueCommandCompleteSync(std::unique_ptr<hci::CommandPacketBuilder> command) {
275 ASSERT(command_sync_ == nullptr);
276 command_sync_ = new std::promise<void>();
277 auto command_received = command_sync_->get_future();
278 hci_layer_->EnqueueCommand(std::move(command), handler_->BindOnceOn(this, &impl::OnCommandCompleteSync));
279 command_received.wait();
280 delete command_sync_;
281 command_sync_ = nullptr;
282 }
283
StartOneShotInquiry(bool limited,InquiryLength inquiry_length,NumResponses num_responses)284 void neighbor::InquiryModule::impl::StartOneShotInquiry(
285 bool limited, InquiryLength inquiry_length, NumResponses num_responses) {
286 ASSERT(HasCallbacks());
287 ASSERT(!IsInquiryActive());
288 hci::Lap lap;
289 if (limited) {
290 active_limited_one_shot_ = true;
291 lap.lap_ = kLimitedInquiryAccessCode;
292 } else {
293 active_general_one_shot_ = true;
294 lap.lap_ = kGeneralInquiryAccessCode;
295 }
296 EnqueueCommandStatus(hci::InquiryBuilder::Create(lap, inquiry_length, num_responses));
297 }
298
StopOneShotInquiry()299 void neighbor::InquiryModule::impl::StopOneShotInquiry() {
300 ASSERT(active_general_one_shot_ || active_limited_one_shot_);
301 active_general_one_shot_ = false;
302 active_limited_one_shot_ = false;
303 EnqueueCommandComplete(hci::InquiryCancelBuilder::Create());
304 }
305
StartPeriodicInquiry(bool limited,InquiryLength inquiry_length,NumResponses num_responses,PeriodLength max_delay,PeriodLength min_delay)306 void neighbor::InquiryModule::impl::StartPeriodicInquiry(
307 bool limited,
308 InquiryLength inquiry_length,
309 NumResponses num_responses,
310 PeriodLength max_delay,
311 PeriodLength min_delay) {
312 ASSERT(HasCallbacks());
313 ASSERT(!IsInquiryActive());
314 hci::Lap lap;
315 if (limited) {
316 active_limited_periodic_ = true;
317 lap.lap_ = kLimitedInquiryAccessCode;
318 } else {
319 active_general_periodic_ = true;
320 lap.lap_ = kGeneralInquiryAccessCode;
321 }
322 EnqueueCommandComplete(
323 hci::PeriodicInquiryModeBuilder::Create(max_delay, min_delay, lap, inquiry_length, num_responses));
324 }
325
StopPeriodicInquiry()326 void neighbor::InquiryModule::impl::StopPeriodicInquiry() {
327 ASSERT(active_general_periodic_ || active_limited_periodic_);
328 active_general_periodic_ = false;
329 active_limited_periodic_ = false;
330 EnqueueCommandComplete(hci::ExitPeriodicInquiryModeBuilder::Create());
331 }
332
IsInquiryActive() const333 bool neighbor::InquiryModule::impl::IsInquiryActive() const {
334 return active_general_one_shot_ || active_limited_one_shot_ || active_limited_periodic_ || active_general_periodic_;
335 }
336
Start()337 void neighbor::InquiryModule::impl::Start() {
338 hci_layer_ = module_.GetDependency<hci::HciLayer>();
339 handler_ = module_.GetHandler();
340
341 EnqueueCommandComplete(hci::ReadInquiryResponseTransmitPowerLevelBuilder::Create());
342 EnqueueCommandComplete(hci::ReadInquiryScanActivityBuilder::Create());
343 EnqueueCommandComplete(hci::ReadInquiryScanTypeBuilder::Create());
344 EnqueueCommandCompleteSync(hci::ReadInquiryModeBuilder::Create());
345
346 LOG_DEBUG("Started inquiry module");
347 }
348
Stop()349 void neighbor::InquiryModule::impl::Stop() {
350 LOG_INFO("Inquiry scan interval:%hu window:%hu", inquiry_scan_.interval, inquiry_scan_.window);
351 LOG_INFO(
352 "Inquiry mode:%s scan_type:%s",
353 hci::InquiryModeText(inquiry_mode_).c_str(),
354 hci::InquiryScanTypeText(inquiry_scan_type_).c_str());
355 LOG_INFO("Inquiry response tx power:%hhd", inquiry_response_tx_power_);
356 LOG_DEBUG("Stopped inquiry module");
357 }
358
SetInquiryMode(hci::InquiryMode mode)359 void neighbor::InquiryModule::impl::SetInquiryMode(hci::InquiryMode mode) {
360 EnqueueCommandComplete(hci::WriteInquiryModeBuilder::Create(mode));
361 inquiry_mode_ = mode;
362 LOG_DEBUG("Set inquiry mode:%s", hci::InquiryModeText(mode).c_str());
363 }
364
SetScanActivity(ScanParameters params)365 void neighbor::InquiryModule::impl::SetScanActivity(ScanParameters params) {
366 EnqueueCommandComplete(hci::WriteInquiryScanActivityBuilder::Create(params.interval, params.window));
367 inquiry_scan_ = params;
368 LOG_DEBUG(
369 "Set scan activity interval:0x%x/%.02fms window:0x%x/%.02fms",
370 params.interval,
371 ScanIntervalTimeMs(params.interval),
372 params.window,
373 ScanWindowTimeMs(params.window));
374 }
375
SetScanType(hci::InquiryScanType scan_type)376 void neighbor::InquiryModule::impl::SetScanType(hci::InquiryScanType scan_type) {
377 EnqueueCommandComplete(hci::WriteInquiryScanTypeBuilder::Create(scan_type));
378 LOG_DEBUG("Set scan type:%s", hci::InquiryScanTypeText(scan_type).c_str());
379 }
380
HasCallbacks() const381 bool neighbor::InquiryModule::impl::HasCallbacks() const {
382 return inquiry_callbacks_.result != nullptr && inquiry_callbacks_.result_with_rssi != nullptr &&
383 inquiry_callbacks_.extended_result != nullptr && inquiry_callbacks_.complete != nullptr;
384 }
385
386 /**
387 * General API here
388 */
InquiryModule()389 neighbor::InquiryModule::InquiryModule() : pimpl_(std::make_unique<impl>(*this)) {}
390
~InquiryModule()391 neighbor::InquiryModule::~InquiryModule() {
392 pimpl_.reset();
393 }
394
RegisterCallbacks(InquiryCallbacks callbacks)395 void neighbor::InquiryModule::RegisterCallbacks(InquiryCallbacks callbacks) {
396 pimpl_->RegisterCallbacks(callbacks);
397 }
398
UnregisterCallbacks()399 void neighbor::InquiryModule::UnregisterCallbacks() {
400 pimpl_->UnregisterCallbacks();
401 }
402
StartGeneralInquiry(InquiryLength inquiry_length,NumResponses num_responses)403 void neighbor::InquiryModule::StartGeneralInquiry(InquiryLength inquiry_length, NumResponses num_responses) {
404 GetHandler()->Post(common::BindOnce(
405 &neighbor::InquiryModule::impl::StartOneShotInquiry,
406 common::Unretained(pimpl_.get()),
407 false,
408 inquiry_length,
409 num_responses));
410 }
411
StartLimitedInquiry(InquiryLength inquiry_length,NumResponses num_responses)412 void neighbor::InquiryModule::StartLimitedInquiry(InquiryLength inquiry_length, NumResponses num_responses) {
413 GetHandler()->Post(common::BindOnce(
414 &neighbor::InquiryModule::impl::StartOneShotInquiry,
415 common::Unretained(pimpl_.get()),
416 true,
417 inquiry_length,
418 num_responses));
419 }
420
StopInquiry()421 void neighbor::InquiryModule::StopInquiry() {
422 GetHandler()->Post(
423 common::BindOnce(&neighbor::InquiryModule::impl::StopOneShotInquiry, common::Unretained(pimpl_.get())));
424 }
425
StartGeneralPeriodicInquiry(InquiryLength inquiry_length,NumResponses num_responses,PeriodLength max_delay,PeriodLength min_delay)426 void neighbor::InquiryModule::StartGeneralPeriodicInquiry(
427 InquiryLength inquiry_length, NumResponses num_responses, PeriodLength max_delay, PeriodLength min_delay) {
428 GetHandler()->Post(common::BindOnce(
429 &neighbor::InquiryModule::impl::StartPeriodicInquiry,
430 common::Unretained(pimpl_.get()),
431 false,
432 inquiry_length,
433 num_responses,
434 max_delay,
435 min_delay));
436 }
437
StartLimitedPeriodicInquiry(InquiryLength inquiry_length,NumResponses num_responses,PeriodLength max_delay,PeriodLength min_delay)438 void neighbor::InquiryModule::StartLimitedPeriodicInquiry(
439 InquiryLength inquiry_length, NumResponses num_responses, PeriodLength max_delay, PeriodLength min_delay) {
440 GetHandler()->Post(common::BindOnce(
441 &neighbor::InquiryModule::impl::StartPeriodicInquiry,
442 common::Unretained(pimpl_.get()),
443 true,
444 inquiry_length,
445 num_responses,
446 max_delay,
447 min_delay));
448 }
449
StopPeriodicInquiry()450 void neighbor::InquiryModule::StopPeriodicInquiry() {
451 GetHandler()->Post(
452 common::BindOnce(&neighbor::InquiryModule::impl::StopPeriodicInquiry, common::Unretained(pimpl_.get())));
453 }
454
SetScanActivity(ScanParameters params)455 void neighbor::InquiryModule::SetScanActivity(ScanParameters params) {
456 GetHandler()->Post(
457 common::BindOnce(&neighbor::InquiryModule::impl::SetScanActivity, common::Unretained(pimpl_.get()), params));
458 }
459
SetInterlacedScan()460 void neighbor::InquiryModule::SetInterlacedScan() {
461 GetHandler()->Post(common::BindOnce(
462 &neighbor::InquiryModule::impl::SetScanType, common::Unretained(pimpl_.get()), hci::InquiryScanType::INTERLACED));
463 }
464
SetStandardScan()465 void neighbor::InquiryModule::SetStandardScan() {
466 GetHandler()->Post(common::BindOnce(
467 &neighbor::InquiryModule::impl::SetScanType, common::Unretained(pimpl_.get()), hci::InquiryScanType::STANDARD));
468 }
469
SetStandardInquiryResultMode()470 void neighbor::InquiryModule::SetStandardInquiryResultMode() {
471 GetHandler()->Post(common::BindOnce(
472 &neighbor::InquiryModule::impl::SetInquiryMode, common::Unretained(pimpl_.get()), hci::InquiryMode::STANDARD));
473 }
474
SetInquiryWithRssiResultMode()475 void neighbor::InquiryModule::SetInquiryWithRssiResultMode() {
476 GetHandler()->Post(common::BindOnce(
477 &neighbor::InquiryModule::impl::SetInquiryMode, common::Unretained(pimpl_.get()), hci::InquiryMode::RSSI));
478 }
479
SetExtendedInquiryResultMode()480 void neighbor::InquiryModule::SetExtendedInquiryResultMode() {
481 GetHandler()->Post(common::BindOnce(
482 &neighbor::InquiryModule::impl::SetInquiryMode,
483 common::Unretained(pimpl_.get()),
484 hci::InquiryMode::RSSI_OR_EXTENDED));
485 }
486
487 /**
488 * Module methods here
489 */
ListDependencies(ModuleList * list)490 void neighbor::InquiryModule::ListDependencies(ModuleList* list) {
491 list->add<hci::HciLayer>();
492 }
493
Start()494 void neighbor::InquiryModule::Start() {
495 pimpl_->Start();
496 }
497
Stop()498 void neighbor::InquiryModule::Stop() {
499 pimpl_->Stop();
500 }
501
502 } // namespace neighbor
503 } // namespace bluetooth
504