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 "hal/snoop_logger.h"
18
19 #include <arpa/inet.h>
20 #include <sys/stat.h>
21
22 #include <algorithm>
23 #include <bitset>
24 #include <chrono>
25 #include <sstream>
26
27 #include "common/circular_buffer.h"
28 #include "common/init_flags.h"
29 #include "common/strings.h"
30 #include "hal/snoop_logger_common.h"
31 #include "hal/syscall_wrapper_impl.h"
32 #include "os/fake_timer/fake_timerfd.h"
33 #include "os/files.h"
34 #include "os/log.h"
35 #include "os/parameter_provider.h"
36 #include "os/system_properties.h"
37
38 namespace bluetooth {
39 #ifdef USE_FAKE_TIMERS
40 using os::fake_timer::fake_timerfd_get_clock;
41 #endif
42 namespace hal {
43
44 // Adds L2CAP channel to acceptlist.
AddL2capCid(uint16_t local_cid,uint16_t remote_cid)45 void FilterTracker::AddL2capCid(uint16_t local_cid, uint16_t remote_cid) {
46 l2c_local_cid.insert(local_cid);
47 l2c_remote_cid.insert(remote_cid);
48 }
49
50 // Sets L2CAP channel that RFCOMM uses.
SetRfcommCid(uint16_t local_cid,uint16_t remote_cid)51 void FilterTracker::SetRfcommCid(uint16_t local_cid, uint16_t remote_cid) {
52 rfcomm_local_cid = local_cid;
53 rfcomm_remote_cid = remote_cid;
54 }
55
56 // Remove L2CAP channel from acceptlist.
RemoveL2capCid(uint16_t local_cid,uint16_t remote_cid)57 void FilterTracker::RemoveL2capCid(uint16_t local_cid, uint16_t remote_cid) {
58 if (rfcomm_local_cid == local_cid) {
59 rfcomm_channels.clear();
60 rfcomm_channels.insert(0);
61 rfcomm_local_cid = 0;
62 rfcomm_remote_cid = 0;
63 }
64
65 l2c_local_cid.erase(local_cid);
66 l2c_remote_cid.erase(remote_cid);
67 }
68
AddRfcommDlci(uint8_t channel)69 void FilterTracker::AddRfcommDlci(uint8_t channel) {
70 rfcomm_channels.insert(channel);
71 }
72
IsAcceptlistedL2cap(bool local,uint16_t cid)73 bool FilterTracker::IsAcceptlistedL2cap(bool local, uint16_t cid) {
74 const auto& set = local ? l2c_local_cid : l2c_remote_cid;
75 return (set.find(cid) != set.end());
76 }
77
IsRfcommChannel(bool local,uint16_t cid)78 bool FilterTracker::IsRfcommChannel(bool local, uint16_t cid) {
79 const auto& channel = local ? rfcomm_local_cid : rfcomm_remote_cid;
80 return cid == channel;
81 }
82
IsAcceptlistedDlci(uint8_t dlci)83 bool FilterTracker::IsAcceptlistedDlci(uint8_t dlci) {
84 return rfcomm_channels.find(dlci) != rfcomm_channels.end();
85 }
86
SetupProfilesFilter(bool pbap_filtered,bool map_filtered)87 void ProfilesFilter::SetupProfilesFilter(bool pbap_filtered, bool map_filtered) {
88 if (setup_done_flag) {
89 return;
90 }
91 setup_done_flag = true;
92
93 LOG_DEBUG("SetupProfilesFilter: pbap=%d, map=%d", pbap_filtered, map_filtered);
94
95 for (int i = 0; i < FILTER_PROFILE_MAX; i++) {
96 profiles[i].type = (profile_type_t)i;
97 profiles[i].enabled = false;
98 profiles[i].rfcomm_opened = false;
99 profiles[i].l2cap_opened = false;
100 }
101
102 if (pbap_filtered) {
103 profiles[FILTER_PROFILE_PBAP].enabled = profiles[FILTER_PROFILE_HFP_HS].enabled =
104 profiles[FILTER_PROFILE_HFP_HF].enabled = true;
105 }
106 if (map_filtered) {
107 profiles[FILTER_PROFILE_MAP].enabled = true;
108 }
109 ch_rfc_l = ch_rfc_r = ch_last = 0;
110
111 PrintProfilesConfig();
112 }
113
IsHfpProfile(bool local,uint16_t cid,uint8_t dlci)114 bool ProfilesFilter::IsHfpProfile(bool local, uint16_t cid, uint8_t dlci) {
115 profile_type_t profile = DlciToProfile(local, cid, dlci);
116 return profile == FILTER_PROFILE_HFP_HS || profile == FILTER_PROFILE_HFP_HF;
117 }
118
IsL2capFlowExt(bool local,uint16_t cid)119 bool ProfilesFilter::IsL2capFlowExt(bool local, uint16_t cid) {
120 profile_type_t profile = CidToProfile(local, cid);
121 if (profile >= 0) return profiles[profile].flow_ext_l2cap;
122 return false;
123 }
124
IsRfcommFlowExt(bool local,uint16_t cid,uint8_t dlci)125 bool ProfilesFilter::IsRfcommFlowExt(bool local, uint16_t cid, uint8_t dlci) {
126 profile_type_t profile = DlciToProfile(local, cid, dlci);
127 if (profile >= 0) current_profile = profile;
128 return profiles[profile].flow_ext_rfcomm;
129 }
130
CidToProfile(bool local,uint16_t cid)131 profile_type_t ProfilesFilter::CidToProfile(bool local, uint16_t cid) {
132 uint16_t ch;
133 for (int i = 0; i < FILTER_PROFILE_MAX; i++) {
134 if (profiles[i].enabled && profiles[i].l2cap_opened) {
135 ch = local ? profiles[i].lcid : profiles[i].rcid;
136 if (ch == cid) {
137 return (profile_type_t)i;
138 }
139 }
140 }
141 return FILTER_PROFILE_NONE;
142 }
143
DlciToProfile(bool local,uint16_t cid,uint8_t dlci)144 profile_type_t ProfilesFilter::DlciToProfile(bool local, uint16_t cid, uint8_t dlci) {
145 if (!IsRfcommChannel(local, cid)) return FILTER_PROFILE_NONE;
146
147 for (int i = 0; i < FILTER_PROFILE_MAX; i++) {
148 if (profiles[i].enabled && profiles[i].l2cap_opened && profiles[i].rfcomm_opened &&
149 profiles[i].scn == (dlci >> 1)) {
150 return (profile_type_t)i;
151 }
152 }
153 return FILTER_PROFILE_NONE;
154 }
155
ProfileL2capOpen(profile_type_t profile,uint16_t lcid,uint16_t rcid,uint16_t psm,bool flow_ext)156 void ProfilesFilter::ProfileL2capOpen(
157 profile_type_t profile, uint16_t lcid, uint16_t rcid, uint16_t psm, bool flow_ext) {
158 if (profiles[profile].l2cap_opened == true) {
159 LOG_DEBUG("l2cap for %d was already opened. Override it", profile);
160 }
161 LOG_DEBUG(
162 "lcid:=%d, rcid=%d, psm=%d, flow_ext=%d, filter profile=%s",
163 lcid,
164 rcid,
165 psm,
166 flow_ext,
167 ProfilesFilter::ProfileToString(profile).c_str());
168 profiles[profile].lcid = lcid;
169 profiles[profile].rcid = rcid;
170 profiles[profile].psm = psm;
171 profiles[profile].flow_ext_l2cap = flow_ext;
172 profiles[profile].l2cap_opened = true;
173
174 PrintProfilesConfig();
175 }
176
ProfileL2capClose(profile_type_t profile)177 void ProfilesFilter::ProfileL2capClose(profile_type_t profile) {
178 if (profile < 0 || profile >= FILTER_PROFILE_MAX) return;
179 profiles[profile].l2cap_opened = false;
180 }
181
ProfileRfcommOpen(profile_type_t profile,uint16_t lcid,uint8_t dlci,uint16_t uuid,bool flow_ext)182 void ProfilesFilter::ProfileRfcommOpen(
183 profile_type_t profile, uint16_t lcid, uint8_t dlci, uint16_t uuid, bool flow_ext) {
184 if (profiles[profile].rfcomm_opened == true) {
185 LOG_DEBUG("rfcomm for %d was already opened. Override it", profile);
186 }
187 LOG_DEBUG(
188 "lcid:=%d, dlci=%d, uuid=%d, flow_ext=%d, filter profile=%s",
189 lcid,
190 dlci,
191 uuid,
192 flow_ext,
193 ProfilesFilter::ProfileToString(profile).c_str());
194 profiles[profile].rfcomm_uuid = uuid;
195 profiles[profile].scn = (dlci >> 1);
196 profiles[profile].flow_ext_rfcomm = flow_ext;
197 profiles[profile].l2cap_opened = true;
198 profiles[profile].rfcomm_opened = true;
199
200 PrintProfilesConfig();
201 }
202
ProfileRfcommClose(profile_type_t profile)203 void ProfilesFilter::ProfileRfcommClose(profile_type_t profile) {
204 if (profile < 0 || profile >= FILTER_PROFILE_MAX) return;
205 profiles[profile].rfcomm_opened = false;
206 }
207
IsRfcommChannel(bool local,uint16_t cid)208 bool ProfilesFilter::IsRfcommChannel(bool local, uint16_t cid) {
209 uint16_t channel = local ? ch_rfc_l : ch_rfc_r;
210 return cid == channel;
211 }
212
PrintProfilesConfig()213 void ProfilesFilter::PrintProfilesConfig() {
214 for (int i = 0; i < FILTER_PROFILE_MAX; i++) {
215 if (profiles[i].enabled) {
216 LOG_DEBUG(
217 "\ntype: %s \
218 \nenabled: %d, l2cap_opened: %d, rfcomm_opened: %d\
219 \nflow_ext_l2cap: %d, flow_ext_rfcomm: %d\
220 \nlcid: %d, rcid: %d, rfcomm_uuid: %d, psm: %d\
221 \nscn: %d \n",
222 ProfilesFilter::ProfileToString(profiles[i].type).c_str(),
223 profiles[i].enabled,
224 profiles[i].l2cap_opened,
225 profiles[i].rfcomm_opened,
226 profiles[i].flow_ext_l2cap,
227 profiles[i].flow_ext_rfcomm,
228 profiles[i].lcid,
229 profiles[i].rcid,
230 profiles[i].rfcomm_uuid,
231 profiles[i].psm,
232 profiles[i].psm);
233 }
234 }
235 }
236
237 namespace {
238
239 // Epoch in microseconds since 01/01/0000.
240 constexpr uint64_t kBtSnoopEpochDelta = 0x00dcddb30f2f8000ULL;
241
242 // Qualcomm debug logs handle
243 constexpr uint16_t kQualcommDebugLogHandle = 0xedc;
244
245 // Number of bytes into a packet where you can find the value for a channel.
246 constexpr size_t ACL_CHANNEL_OFFSET = 0;
247 constexpr size_t ACL_LENGTH_OFFSET = 2;
248 constexpr size_t L2CAP_PDU_LENGTH_OFFSET = 4;
249 constexpr size_t L2CAP_CHANNEL_OFFSET = 6;
250 constexpr size_t L2CAP_CONTROL_OFFSET = 8;
251 constexpr size_t RFCOMM_CHANNEL_OFFSET = 8;
252 constexpr size_t RFCOMM_EVENT_OFFSET = 9;
253
254 // RFCOMM filtering consts
255 constexpr uint8_t RFCOMM_SABME = 0x2F; // RFCOMM: Start Asynchronous Balanced Mode (startup cmd)
256 constexpr uint8_t RFCOMM_UA = 0x63; // RFCOMM: Unnumbered Acknowledgement (rsp when connected)
257 constexpr uint8_t RFCOMM_UIH = 0xEF; // RFCOMM: Unnumbered Information with Header check
258
259 constexpr uint8_t START_PACKET_BOUNDARY = 0x02;
260 constexpr uint8_t CONTINUATION_PACKET_BOUNDARY = 0x01;
261 constexpr uint16_t HANDLE_MASK = 0x0FFF;
__anonfecfc9330202(auto handle) 262 auto GetBoundaryFlag = [](auto handle) { return (((handle) >> 12) & 0x0003); };
263
264 // ProfilesFilter consts
265 constexpr size_t ACL_HEADER_LENGTH = 4;
266 constexpr size_t BASIC_L2CAP_HEADER_LENGTH = 4;
267 constexpr uint8_t EXTRA_BUF_SIZE = 0x40;
268 constexpr uint16_t DEFAULT_PACKET_SIZE = 0x800;
269
270 constexpr uint8_t PROFILE_SCN_PBAP = 19;
271 constexpr uint8_t PROFILE_SCN_MAP = 26;
272
273 constexpr uint16_t PROFILE_PSM_PBAP = 0x1025;
274 constexpr uint16_t PROFILE_PSM_MAP = 0x1029;
275 constexpr uint16_t PROFILE_PSM_RFCOMM = 0x0003;
276
277 constexpr uint16_t PROFILE_UUID_PBAP = 0x112f;
278 constexpr uint16_t PROFILE_UUID_MAP = 0x1132;
279 constexpr uint16_t PROFILE_UUID_HFP_HS = 0x1112;
280 constexpr uint16_t PROFILE_UUID_HFP_HF = 0x111f;
281
htonll(uint64_t ll)282 uint64_t htonll(uint64_t ll) {
283 if constexpr (isLittleEndian) {
284 return static_cast<uint64_t>(htonl(ll & 0xffffffff)) << 32 | htonl(ll >> 32);
285 } else {
286 return ll;
287 }
288 }
289
290 // The number of packets per btsnoop file before we rotate to the next file. As of right now there
291 // are two snoop files that are rotated through. The size can be dynamically configured by setting
292 // the relevant system property
293 constexpr size_t kDefaultBtSnoopMaxPacketsPerFile = 0xffff;
294
295 // We restrict the maximum packet size to 150 bytes
296 constexpr size_t kDefaultBtSnoozMaxBytesPerPacket = 150;
297 constexpr size_t kDefaultBtSnoozMaxPayloadBytesPerPacket =
298 kDefaultBtSnoozMaxBytesPerPacket - sizeof(SnoopLogger::PacketHeaderType);
299
300 using namespace std::chrono_literals;
301 constexpr std::chrono::hours kBtSnoozLogLifeTime = 12h;
302 constexpr std::chrono::hours kBtSnoozLogDeleteRepeatingAlarmInterval = 1h;
303
304 std::mutex filter_tracker_list_mutex;
305 std::unordered_map<uint16_t, FilterTracker> filter_tracker_list;
306 std::unordered_map<uint16_t, uint16_t> local_cid_to_acl;
307
308 std::mutex a2dpMediaChannels_mutex;
309 std::vector<SnoopLogger::A2dpMediaChannel> a2dpMediaChannels;
310
311 std::mutex snoop_log_filters_mutex;
312
313 std::mutex profiles_filter_mutex;
314 std::unordered_map<int16_t, ProfilesFilter> profiles_filter_table;
315 constexpr const char* payload_fill_magic = "PROHIBITED";
316 constexpr const char* cpbr_pattern = "\x0d\x0a+CPBR:";
317 constexpr const char* clcc_pattern = "\x0d\x0a+CLCC:";
318 const uint32_t magic_pat_len = strlen(payload_fill_magic);
319 const uint32_t cpbr_pat_len = strlen(cpbr_pattern);
320 const uint32_t clcc_pat_len = strlen(clcc_pattern);
321
get_btsnoop_log_path(std::string log_dir,bool filtered)322 std::string get_btsnoop_log_path(std::string log_dir, bool filtered) {
323 if (filtered) {
324 log_dir.append(".filtered");
325 }
326 return log_dir;
327 }
328
get_last_log_path(std::string log_file_path)329 std::string get_last_log_path(std::string log_file_path) {
330 return log_file_path.append(".last");
331 }
332
delete_btsnoop_files(const std::string & log_path)333 void delete_btsnoop_files(const std::string& log_path) {
334 LOG_INFO("Deleting logs if they exist");
335 if (os::FileExists(log_path)) {
336 if (!os::RemoveFile(log_path)) {
337 LOG_ERROR("Failed to remove main log file at \"%s\"", log_path.c_str());
338 }
339 } else {
340 LOG_INFO("Main log file does not exist at \"%s\"", log_path.c_str());
341 }
342 auto last_log_path = get_last_log_path(log_path);
343 if (os::FileExists(last_log_path)) {
344 if (!os::RemoveFile(last_log_path)) {
345 LOG_ERROR("Failed to remove last log file at \"%s\"", log_path.c_str());
346 }
347 } else {
348 LOG_INFO("Last log file does not exist at \"%s\"", log_path.c_str());
349 }
350 }
351
delete_old_btsnooz_files(const std::string & log_path,const std::chrono::milliseconds log_life_time)352 void delete_old_btsnooz_files(const std::string& log_path, const std::chrono::milliseconds log_life_time) {
353 auto opt_created_ts = os::FileCreatedTime(log_path);
354 if (!opt_created_ts) return;
355 #ifdef USE_FAKE_TIMERS
356 auto diff = fake_timerfd_get_clock() - file_creation_time;
357 uint64_t log_lifetime = log_life_time.count();
358 if (diff >= log_lifetime) {
359 #else
360 using namespace std::chrono;
361 auto created_tp = opt_created_ts.value();
362 auto current_tp = std::chrono::system_clock::now();
363
364 auto diff = duration_cast<milliseconds>(current_tp - created_tp);
365 if (diff >= log_life_time) {
366 #endif
367 delete_btsnoop_files(log_path);
368 }
369 }
370
371 size_t get_btsnooz_packet_length_to_write(
372 const HciPacket& packet, SnoopLogger::PacketType type, bool qualcomm_debug_log_enabled) {
373 static const size_t kAclHeaderSize = 4;
374 static const size_t kL2capHeaderSize = 4;
375 static const size_t kL2capCidOffset = (kAclHeaderSize + 2);
376 static const uint16_t kL2capSignalingCid = 0x0001;
377
378 static const size_t kHciAclHandleOffset = 0;
379
380 // Maximum amount of ACL data to log.
381 // Enough for an RFCOMM frame up to the frame check;
382 // not enough for a HID report or audio data.
383 static const size_t kMaxBtsnoozAclSize = 14;
384
385 // Calculate packet length to be included
386 size_t included_length = 0;
387 switch (type) {
388 case SnoopLogger::PacketType::CMD:
389 case SnoopLogger::PacketType::EVT:
390 included_length = packet.size();
391 break;
392
393 case SnoopLogger::PacketType::ACL: {
394 // Log ACL and L2CAP header by default
395 size_t len_hci_acl = kAclHeaderSize + kL2capHeaderSize;
396 // Check if we have enough data for an L2CAP header
397 if (packet.size() > len_hci_acl) {
398 uint16_t l2cap_cid =
399 static_cast<uint16_t>(packet[kL2capCidOffset]) |
400 static_cast<uint16_t>((static_cast<uint16_t>(packet[kL2capCidOffset + 1]) << static_cast<uint16_t>(8)));
401 uint16_t hci_acl_packet_handle =
402 static_cast<uint16_t>(packet[kHciAclHandleOffset]) |
403 static_cast<uint16_t>((static_cast<uint16_t>(packet[kHciAclHandleOffset + 1]) << static_cast<uint16_t>(8)));
404 hci_acl_packet_handle &= 0x0fff;
405
406 if (l2cap_cid == kL2capSignalingCid) {
407 // For the signaling CID, take the full packet.
408 // That way, the PSM setup is captured, allowing decoding of PSMs down
409 // the road.
410 return packet.size();
411 } else if (qualcomm_debug_log_enabled && hci_acl_packet_handle == kQualcommDebugLogHandle) {
412 return packet.size();
413 } else {
414 // Otherwise, return as much as we reasonably can
415 len_hci_acl = kMaxBtsnoozAclSize;
416 }
417 }
418 included_length = std::min(len_hci_acl, packet.size());
419 break;
420 }
421
422 case SnoopLogger::PacketType::ISO:
423 case SnoopLogger::PacketType::SCO:
424 default:
425 // We are not logging SCO and ISO packets in snooz log as they may contain voice data
426 break;
427 }
428 return std::min(included_length, kDefaultBtSnoozMaxPayloadBytesPerPacket);
429 }
430
431 } // namespace
432
433 // system properties
434 const std::string SnoopLogger::kBtSnoopMaxPacketsPerFileProperty = "persist.bluetooth.btsnoopsize";
435 const std::string SnoopLogger::kIsDebuggableProperty = "ro.debuggable";
436 const std::string SnoopLogger::kBtSnoopLogModeProperty = "persist.bluetooth.btsnooplogmode";
437 const std::string SnoopLogger::kBtSnoopDefaultLogModeProperty = "persist.bluetooth.btsnoopdefaultmode";
438 const std::string SnoopLogger::kBtSnoopLogPersists = "persist.bluetooth.btsnooplogpersists";
439 // Truncates ACL packets (non-fragment) to fixed (MAX_HCI_ACL_LEN) number of bytes
440 const std::string SnoopLogger::kBtSnoopLogFilterHeadersProperty =
441 "persist.bluetooth.snooplogfilter.headers.enabled";
442 // Discards A2DP media packets (non-split mode)
443 const std::string SnoopLogger::kBtSnoopLogFilterProfileA2dpProperty =
444 "persist.bluetooth.snooplogfilter.profiles.a2dp.enabled";
445 // Filters MAP packets based on the filter mode
446 const std::string SnoopLogger::kBtSnoopLogFilterProfileMapModeProperty =
447 "persist.bluetooth.snooplogfilter.profiles.map";
448 // Filters PBAP and HFP packets (CPBR, CLCC) based on the filter mode
449 const std::string SnoopLogger::kBtSnoopLogFilterProfilePbapModeProperty =
450 "persist.bluetooth.snooplogfilter.profiles.pbap";
451 // Truncates RFCOMM UIH packet to fixed (L2CAP_HEADER_SIZE) number of bytes
452 const std::string SnoopLogger::kBtSnoopLogFilterProfileRfcommProperty =
453 "persist.bluetooth.snooplogfilter.profiles.rfcomm.enabled";
454 const std::string SnoopLogger::kSoCManufacturerProperty = "ro.soc.manufacturer";
455
456 // persist.bluetooth.btsnooplogmode
457 const std::string SnoopLogger::kBtSnoopLogModeDisabled = "disabled";
458 const std::string SnoopLogger::kBtSnoopLogModeFiltered = "filtered";
459 const std::string SnoopLogger::kBtSnoopLogModeFull = "full";
460 // ro.soc.manufacturer
461 const std::string SnoopLogger::kSoCManufacturerQualcomm = "Qualcomm";
462
463 // PBAP, MAP and HFP packets filter mode - discard whole packet
464 const std::string SnoopLogger::kBtSnoopLogFilterProfileModeFullfillter = "fullfilter";
465 // PBAP, MAP and HFP packets filter mode - truncate to fixed length
466 const std::string SnoopLogger::kBtSnoopLogFilterProfileModeHeader = "header";
467 // PBAP, MAP and HFP packets filter mode - fill with a magic string, such as: "PROHIBITED"
468 const std::string SnoopLogger::kBtSnoopLogFilterProfileModeMagic = "magic";
469 // PBAP, MAP and HFP packets filter mode - disabled
470 const std::string SnoopLogger::kBtSnoopLogFilterProfileModeDisabled = "disabled";
471
472 std::string SnoopLogger::btsnoop_mode_;
473
474 // Consts accessible in unit tests
475 const size_t SnoopLogger::PACKET_TYPE_LENGTH = 1;
476 const size_t SnoopLogger::MAX_HCI_ACL_LEN = 14;
477 const uint32_t SnoopLogger::L2CAP_HEADER_SIZE = 8;
478
SnoopLogger(std::string snoop_log_path,std::string snooz_log_path,size_t max_packets_per_file,size_t max_packets_per_buffer,const std::string & btsnoop_mode,bool qualcomm_debug_log_enabled,const std::chrono::milliseconds snooz_log_life_time,const std::chrono::milliseconds snooz_log_delete_alarm_interval,bool snoop_log_persists)479 SnoopLogger::SnoopLogger(
480 std::string snoop_log_path,
481 std::string snooz_log_path,
482 size_t max_packets_per_file,
483 size_t max_packets_per_buffer,
484 const std::string& btsnoop_mode,
485 bool qualcomm_debug_log_enabled,
486 const std::chrono::milliseconds snooz_log_life_time,
487 const std::chrono::milliseconds snooz_log_delete_alarm_interval,
488 bool snoop_log_persists)
489 : snoop_log_path_(std::move(snoop_log_path)),
490 snooz_log_path_(std::move(snooz_log_path)),
491 max_packets_per_file_(max_packets_per_file),
492 btsnooz_buffer_(max_packets_per_buffer),
493 qualcomm_debug_log_enabled_(qualcomm_debug_log_enabled),
494 snooz_log_life_time_(snooz_log_life_time),
495 snooz_log_delete_alarm_interval_(snooz_log_delete_alarm_interval),
496 snoop_log_persists(snoop_log_persists) {
497 btsnoop_mode_ = btsnoop_mode;
498
499 if (btsnoop_mode_ == kBtSnoopLogModeFiltered &&
500 !bluetooth::common::InitFlags::IsSnoopLoggerFilteringEnabled()) {
501 btsnoop_mode_ = kBtSnoopLogModeDisabled;
502 }
503
504 if (btsnoop_mode_ == kBtSnoopLogModeFiltered) {
505 LOG_INFO("Snoop Logs filtered mode enabled");
506 EnableFilters();
507 // delete unfiltered logs
508 delete_btsnoop_files(get_btsnoop_log_path(snoop_log_path_, false));
509 // delete snooz logs
510 delete_btsnoop_files(snooz_log_path_);
511 } else if (btsnoop_mode_ == kBtSnoopLogModeFull) {
512 LOG_INFO("Snoop Logs full mode enabled");
513 if (!snoop_log_persists) {
514 // delete filtered logs
515 delete_btsnoop_files(get_btsnoop_log_path(snoop_log_path_, true));
516 // delete snooz logs
517 delete_btsnoop_files(snooz_log_path_);
518 }
519 } else {
520 LOG_INFO("Snoop Logs disabled");
521 // delete both filtered and unfiltered logs
522 delete_btsnoop_files(get_btsnoop_log_path(snoop_log_path_, true));
523 delete_btsnoop_files(get_btsnoop_log_path(snoop_log_path_, false));
524 }
525
526 snoop_logger_socket_thread_ = nullptr;
527 socket_ = nullptr;
528 // Add ".filtered" extension if necessary
529 snoop_log_path_ = get_btsnoop_log_path(snoop_log_path_, btsnoop_mode_ == kBtSnoopLogModeFiltered);
530 }
531
CloseCurrentSnoopLogFile()532 void SnoopLogger::CloseCurrentSnoopLogFile() {
533 std::lock_guard<std::recursive_mutex> lock(file_mutex_);
534 if (btsnoop_ostream_.is_open()) {
535 btsnoop_ostream_.flush();
536 btsnoop_ostream_.close();
537 }
538 packet_counter_ = 0;
539 }
540
OpenNextSnoopLogFile()541 void SnoopLogger::OpenNextSnoopLogFile() {
542 std::lock_guard<std::recursive_mutex> lock(file_mutex_);
543 CloseCurrentSnoopLogFile();
544
545 auto last_file_path = get_last_log_path(snoop_log_path_);
546
547 if (os::FileExists(snoop_log_path_)) {
548 if (!os::RenameFile(snoop_log_path_, last_file_path)) {
549 LOG_ERROR(
550 "Unabled to rename existing snoop log from \"%s\" to \"%s\"",
551 snoop_log_path_.c_str(),
552 last_file_path.c_str());
553 }
554 } else {
555 LOG_INFO("Previous log file \"%s\" does not exist, skip renaming", snoop_log_path_.c_str());
556 }
557
558 mode_t prevmask = umask(0);
559 // do not use std::ios::app as we want override the existing file
560 btsnoop_ostream_.open(snoop_log_path_, std::ios::binary | std::ios::out);
561 #ifdef USE_FAKE_TIMERS
562 file_creation_time = fake_timerfd_get_clock();
563 #endif
564 if (!btsnoop_ostream_.good()) {
565 LOG_ALWAYS_FATAL("Unable to open snoop log at \"%s\", error: \"%s\"", snoop_log_path_.c_str(), strerror(errno));
566 }
567 umask(prevmask);
568 if (!btsnoop_ostream_.write(
569 reinterpret_cast<const char*>(&SnoopLoggerCommon::kBtSnoopFileHeader),
570 sizeof(SnoopLoggerCommon::FileHeaderType))) {
571 LOG_ALWAYS_FATAL("Unable to write file header to \"%s\", error: \"%s\"", snoop_log_path_.c_str(), strerror(errno));
572 }
573 if (!btsnoop_ostream_.flush()) {
574 LOG_ERROR("Failed to flush, error: \"%s\"", strerror(errno));
575 }
576 }
577
EnableFilters()578 void SnoopLogger::EnableFilters() {
579 if (btsnoop_mode_ != kBtSnoopLogModeFiltered) {
580 return;
581 }
582 std::lock_guard<std::mutex> lock(snoop_log_filters_mutex);
583 for (auto itr = kBtSnoopLogFilterState.begin(); itr != kBtSnoopLogFilterState.end(); itr++) {
584 auto filter_enabled_property = os::GetSystemProperty(itr->first);
585 if (filter_enabled_property) {
586 itr->second = filter_enabled_property.value() == "true";
587 }
588 LOG_INFO("%s: %d", itr->first.c_str(), itr->second);
589 }
590 for (auto itr = kBtSnoopLogFilterMode.begin(); itr != kBtSnoopLogFilterMode.end(); itr++) {
591 auto filter_mode_property = os::GetSystemProperty(itr->first);
592 if (filter_mode_property) {
593 itr->second = filter_mode_property.value();
594 } else {
595 itr->second = SnoopLogger::kBtSnoopLogFilterProfileModeDisabled;
596 }
597 LOG_INFO("%s: %s", itr->first.c_str(), itr->second.c_str());
598 }
599 }
600
DisableFilters()601 void SnoopLogger::DisableFilters() {
602 std::lock_guard<std::mutex> lock(snoop_log_filters_mutex);
603 for (auto itr = kBtSnoopLogFilterState.begin(); itr != kBtSnoopLogFilterState.end(); itr++) {
604 itr->second = false;
605 LOG_INFO("%s, %d", itr->first.c_str(), itr->second);
606 }
607 for (auto itr = kBtSnoopLogFilterMode.begin(); itr != kBtSnoopLogFilterMode.end(); itr++) {
608 itr->second = SnoopLogger::kBtSnoopLogFilterProfileModeDisabled;
609 LOG_INFO("%s, %s", itr->first.c_str(), itr->second.c_str());
610 }
611 }
612
IsFilterEnabled(std::string filter_name)613 bool SnoopLogger::IsFilterEnabled(std::string filter_name) {
614 std::lock_guard<std::mutex> lock(snoop_log_filters_mutex);
615 for (auto itr = kBtSnoopLogFilterState.begin(); itr != kBtSnoopLogFilterState.end(); itr++) {
616 if (filter_name == itr->first) {
617 return itr->second == true;
618 }
619 }
620 for (auto itr = kBtSnoopLogFilterMode.begin(); itr != kBtSnoopLogFilterMode.end(); itr++) {
621 if (filter_name == itr->first) {
622 return itr->second != SnoopLogger::kBtSnoopLogFilterProfileModeDisabled;
623 }
624 }
625 return false;
626 }
627
ShouldFilterLog(bool is_received,uint8_t * packet)628 bool SnoopLogger::ShouldFilterLog(bool is_received, uint8_t* packet) {
629 uint16_t conn_handle =
630 ((((uint16_t)packet[ACL_CHANNEL_OFFSET + 1]) << 8) + packet[ACL_CHANNEL_OFFSET]) & 0x0fff;
631 std::lock_guard<std::mutex> lock(filter_tracker_list_mutex);
632 auto& filters = filter_tracker_list[conn_handle];
633 uint16_t cid = (packet[L2CAP_CHANNEL_OFFSET + 1] << 8) + packet[L2CAP_CHANNEL_OFFSET];
634 if (filters.IsRfcommChannel(is_received, cid)) {
635 uint8_t rfcomm_event = packet[RFCOMM_EVENT_OFFSET] & 0b11101111;
636 if (rfcomm_event == RFCOMM_SABME || rfcomm_event == RFCOMM_UA) {
637 return false;
638 }
639
640 uint8_t rfcomm_dlci = packet[RFCOMM_CHANNEL_OFFSET] >> 2;
641 if (!filters.IsAcceptlistedDlci(rfcomm_dlci)) {
642 return true;
643 }
644 } else if (!filters.IsAcceptlistedL2cap(is_received, cid)) {
645 return true;
646 }
647
648 return false;
649 }
650
CalculateAclPacketLength(uint32_t & length,uint8_t * packet,bool is_received)651 void SnoopLogger::CalculateAclPacketLength(uint32_t& length, uint8_t* packet, bool is_received) {
652 uint32_t def_len =
653 ((((uint16_t)packet[ACL_LENGTH_OFFSET + 1]) << 8) + packet[ACL_LENGTH_OFFSET]) +
654 ACL_HEADER_LENGTH + PACKET_TYPE_LENGTH;
655 constexpr uint16_t L2CAP_SIGNALING_CID = 0x0001;
656
657 if (length == 0) {
658 return;
659 }
660
661 uint16_t handle =
662 ((((uint16_t)packet[ACL_CHANNEL_OFFSET + 1]) << 8) + packet[ACL_CHANNEL_OFFSET]);
663 uint8_t boundary_flag = GetBoundaryFlag(handle);
664 handle = handle & HANDLE_MASK;
665
666 if (boundary_flag == START_PACKET_BOUNDARY) {
667 uint16_t l2cap_cid = packet[L2CAP_CHANNEL_OFFSET] | (packet[L2CAP_CHANNEL_OFFSET + 1] << 8);
668 if (l2cap_cid == L2CAP_SIGNALING_CID || handle == kQualcommDebugLogHandle) {
669 length = def_len;
670 } else {
671 if (def_len < MAX_HCI_ACL_LEN) {
672 length = def_len;
673 } else {
674 // Otherwise, return as much as we reasonably can
675 length = MAX_HCI_ACL_LEN;
676 }
677 }
678 }
679 }
680
PayloadStrip(profile_type_t current_profile,uint8_t * packet,uint32_t hdr_len,uint32_t pl_len)681 uint32_t SnoopLogger::PayloadStrip(
682 profile_type_t current_profile, uint8_t* packet, uint32_t hdr_len, uint32_t pl_len) {
683 uint32_t len = 0;
684 std::string profile_filter_mode = "";
685 LOG_DEBUG(
686 "current_profile=%s, hdr len=%d, total len=%d",
687 ProfilesFilter::ProfileToString(current_profile).c_str(),
688 hdr_len,
689 pl_len);
690 std::lock_guard<std::mutex> lock(snoop_log_filters_mutex);
691 switch (current_profile) {
692 case FILTER_PROFILE_PBAP:
693 case FILTER_PROFILE_HFP_HF:
694 case FILTER_PROFILE_HFP_HS:
695 profile_filter_mode =
696 kBtSnoopLogFilterMode[SnoopLogger::kBtSnoopLogFilterProfilePbapModeProperty];
697 break;
698 case FILTER_PROFILE_MAP:
699 profile_filter_mode =
700 kBtSnoopLogFilterMode[SnoopLogger::kBtSnoopLogFilterProfileMapModeProperty];
701 break;
702 default:
703 profile_filter_mode = kBtSnoopLogFilterProfileModeDisabled;
704 }
705
706 if (profile_filter_mode == SnoopLogger::kBtSnoopLogFilterProfileModeFullfillter) {
707 return 0;
708 } else if (profile_filter_mode == SnoopLogger::kBtSnoopLogFilterProfileModeHeader) {
709 len = hdr_len;
710
711 packet[ACL_LENGTH_OFFSET] = static_cast<uint8_t>(hdr_len - BASIC_L2CAP_HEADER_LENGTH);
712 packet[ACL_LENGTH_OFFSET + 1] =
713 static_cast<uint8_t>((hdr_len - BASIC_L2CAP_HEADER_LENGTH) >> 8);
714
715 packet[L2CAP_PDU_LENGTH_OFFSET] =
716 static_cast<uint8_t>(hdr_len - (ACL_HEADER_LENGTH + BASIC_L2CAP_HEADER_LENGTH));
717 packet[L2CAP_PDU_LENGTH_OFFSET + 1] =
718 static_cast<uint8_t>((hdr_len - (ACL_HEADER_LENGTH + BASIC_L2CAP_HEADER_LENGTH)) >> 8);
719
720 } else if (profile_filter_mode == SnoopLogger::kBtSnoopLogFilterProfileModeMagic) {
721 strcpy(reinterpret_cast<char*>(&packet[hdr_len]), payload_fill_magic);
722
723 packet[ACL_LENGTH_OFFSET] =
724 static_cast<uint8_t>(hdr_len + magic_pat_len - BASIC_L2CAP_HEADER_LENGTH);
725 packet[ACL_LENGTH_OFFSET + 1] =
726 static_cast<uint8_t>((hdr_len + magic_pat_len - BASIC_L2CAP_HEADER_LENGTH) >> 8);
727
728 packet[L2CAP_PDU_LENGTH_OFFSET] = static_cast<uint8_t>(
729 hdr_len + magic_pat_len - (ACL_HEADER_LENGTH + BASIC_L2CAP_HEADER_LENGTH));
730 packet[L2CAP_PDU_LENGTH_OFFSET + 1] = static_cast<uint8_t>(
731 (hdr_len + magic_pat_len - (ACL_HEADER_LENGTH + BASIC_L2CAP_HEADER_LENGTH)) >> 8);
732
733 len = hdr_len + magic_pat_len;
734 } else {
735 // Return unchanged
736 len = hdr_len + pl_len;
737 }
738 return len + PACKET_TYPE_LENGTH; // including packet type byte
739 }
740
FilterProfilesHandleHfp(uint8_t * packet,uint32_t length,uint32_t totlen,uint32_t offset)741 uint32_t SnoopLogger::FilterProfilesHandleHfp(
742 uint8_t* packet, uint32_t length, uint32_t totlen, uint32_t offset) {
743 if ((totlen - offset) > cpbr_pat_len) {
744 if (memcmp(&packet[offset], cpbr_pattern, cpbr_pat_len) == 0) {
745 length = offset + cpbr_pat_len + 1;
746 packet[L2CAP_PDU_LENGTH_OFFSET] = offset + cpbr_pat_len - BASIC_L2CAP_HEADER_LENGTH;
747 packet[L2CAP_PDU_LENGTH_OFFSET] =
748 offset + cpbr_pat_len - (ACL_HEADER_LENGTH + BASIC_L2CAP_HEADER_LENGTH);
749 return length;
750 }
751
752 if (memcmp(&packet[offset], clcc_pattern, clcc_pat_len) == 0) {
753 length = offset + cpbr_pat_len + 1;
754 packet[L2CAP_PDU_LENGTH_OFFSET] = offset + clcc_pat_len - BASIC_L2CAP_HEADER_LENGTH;
755 packet[L2CAP_PDU_LENGTH_OFFSET] =
756 offset + clcc_pat_len - (ACL_HEADER_LENGTH + BASIC_L2CAP_HEADER_LENGTH);
757 }
758 }
759
760 return length;
761 }
762
FilterProfilesRfcommChannel(uint8_t * packet,uint8_t & current_offset,uint32_t & length,profile_type_t & current_profile,bluetooth::hal::ProfilesFilter & filters,bool is_received,uint16_t l2cap_channel,uint32_t & offset,uint32_t total_length)763 void SnoopLogger::FilterProfilesRfcommChannel(
764 uint8_t* packet,
765 uint8_t& current_offset,
766 uint32_t& length,
767 profile_type_t& current_profile,
768 bluetooth::hal::ProfilesFilter& filters,
769 bool is_received,
770 uint16_t l2cap_channel,
771 uint32_t& offset,
772 uint32_t total_length) {
773 uint8_t addr, ctrl, pf;
774
775 addr = packet[current_offset];
776 current_offset += 1;
777 ctrl = packet[RFCOMM_EVENT_OFFSET];
778 current_offset += 1;
779
780 pf = ctrl & 0x10;
781 ctrl = ctrl & 0xef;
782 addr >>= 2;
783 if (ctrl != RFCOMM_UIH) {
784 return;
785 }
786 current_profile = filters.DlciToProfile(is_received, l2cap_channel, addr);
787 if (current_profile != FILTER_PROFILE_NONE) {
788 uint16_t len;
789 uint8_t ea;
790
791 len = packet[current_offset];
792 current_offset += 1;
793 ea = len & 1;
794
795 if (!ea) {
796 current_offset += 1;
797 }
798
799 if (filters.IsRfcommFlowExt(is_received, l2cap_channel, addr) && pf) {
800 current_offset += 1; // credit byte
801 }
802 offset = current_offset;
803
804 if ((filters).IsHfpProfile(is_received, l2cap_channel, addr)) {
805 length = FilterProfilesHandleHfp(packet, length, total_length, offset);
806 } else {
807 length = PayloadStrip(current_profile, packet, offset, total_length - offset);
808 }
809 }
810 }
811
FilterProfiles(bool is_received,uint8_t * packet)812 uint32_t SnoopLogger::FilterProfiles(bool is_received, uint8_t* packet) {
813 bool frag;
814 uint16_t handle, l2c_chan, l2c_ctl;
815 uint32_t length, totlen, offset;
816 uint8_t current_offset = 0;
817 profile_type_t current_profile = FILTER_PROFILE_NONE;
818 constexpr uint16_t L2CAP_SIGNALING_CID = 0x0001;
819
820 std::lock_guard<std::mutex> lock(profiles_filter_mutex);
821
822 handle = ((((uint16_t)packet[ACL_CHANNEL_OFFSET + 1]) << 8) + packet[ACL_CHANNEL_OFFSET]);
823 frag = (GetBoundaryFlag(handle) == CONTINUATION_PACKET_BOUNDARY);
824
825 handle = handle & HANDLE_MASK;
826 current_offset += 2;
827
828 length = (((uint16_t)packet[ACL_LENGTH_OFFSET + 1]) << 8) + packet[ACL_LENGTH_OFFSET];
829 current_offset += 2;
830 totlen = length + ACL_HEADER_LENGTH;
831 length += PACKET_TYPE_LENGTH + ACL_HEADER_LENGTH; // Additional byte is added for packet type
832
833 l2c_chan = ((uint16_t)packet[L2CAP_CHANNEL_OFFSET + 1] << 8) + packet[L2CAP_CHANNEL_OFFSET];
834 current_offset += 4;
835
836 auto& filters = profiles_filter_table[handle];
837 if (frag) {
838 l2c_chan = filters.ch_last;
839 } else {
840 filters.ch_last = l2c_chan;
841 }
842
843 if (l2c_chan != L2CAP_SIGNALING_CID && handle != kQualcommDebugLogHandle) {
844 if (filters.IsL2capFlowExt(is_received, l2c_chan)) {
845 l2c_ctl = ((uint16_t)packet[L2CAP_CONTROL_OFFSET + 1] << 8) + packet[L2CAP_CONTROL_OFFSET];
846 if (!(l2c_ctl & 1)) { // I-Frame
847 if (((l2c_ctl >> 14) & 0x3) == 0x01) { // Start of L2CAP SDU
848 current_offset += 2;
849 }
850 }
851 }
852 offset = current_offset;
853 current_profile = filters.CidToProfile(is_received, l2c_chan);
854 if (current_profile != FILTER_PROFILE_NONE) {
855 if (frag) {
856 return PACKET_TYPE_LENGTH + ACL_HEADER_LENGTH;
857 }
858 return PayloadStrip(current_profile, packet, offset, totlen - offset);
859 }
860
861 if (filters.IsRfcommChannel(is_received, l2c_chan)) {
862 FilterProfilesRfcommChannel(
863 packet,
864 current_offset,
865 length,
866 current_profile,
867 filters,
868 is_received,
869 l2c_chan,
870 offset,
871 totlen);
872 }
873 }
874
875 return length;
876 }
877
AcceptlistL2capChannel(uint16_t conn_handle,uint16_t local_cid,uint16_t remote_cid)878 void SnoopLogger::AcceptlistL2capChannel(
879 uint16_t conn_handle, uint16_t local_cid, uint16_t remote_cid) {
880 if (btsnoop_mode_ != kBtSnoopLogModeFiltered ||
881 !IsFilterEnabled(kBtSnoopLogFilterProfileRfcommProperty)) {
882 return;
883 }
884
885 LOG_DEBUG(
886 "Acceptlisting l2cap channel: conn_handle=%d, local cid=%d, remote cid=%d",
887 conn_handle,
888 local_cid,
889 remote_cid);
890 std::lock_guard<std::mutex> lock(filter_tracker_list_mutex);
891
892 // This will create the entry if there is no associated filter with the
893 // connection.
894 filter_tracker_list[conn_handle].AddL2capCid(local_cid, remote_cid);
895 }
896
AcceptlistRfcommDlci(uint16_t conn_handle,uint16_t local_cid,uint8_t dlci)897 void SnoopLogger::AcceptlistRfcommDlci(uint16_t conn_handle, uint16_t local_cid, uint8_t dlci) {
898 if (btsnoop_mode_ != kBtSnoopLogModeFiltered ||
899 !IsFilterEnabled(kBtSnoopLogFilterProfileRfcommProperty)) {
900 return;
901 }
902
903 LOG_DEBUG("Acceptlisting rfcomm channel: local cid=%d, dlci=%d", local_cid, dlci);
904 std::lock_guard<std::mutex> lock(filter_tracker_list_mutex);
905
906 filter_tracker_list[conn_handle].AddRfcommDlci(dlci);
907 }
908
AddRfcommL2capChannel(uint16_t conn_handle,uint16_t local_cid,uint16_t remote_cid)909 void SnoopLogger::AddRfcommL2capChannel(
910 uint16_t conn_handle, uint16_t local_cid, uint16_t remote_cid) {
911 if (btsnoop_mode_ != kBtSnoopLogModeFiltered ||
912 !IsFilterEnabled(kBtSnoopLogFilterProfileRfcommProperty)) {
913 return;
914 }
915
916 LOG_DEBUG(
917 "Rfcomm data going over l2cap channel: conn_handle=%d local cid=%d remote cid=%d",
918 conn_handle,
919 local_cid,
920 remote_cid);
921 std::lock_guard<std::mutex> lock(filter_tracker_list_mutex);
922
923 filter_tracker_list[conn_handle].SetRfcommCid(local_cid, remote_cid);
924 local_cid_to_acl.insert({local_cid, conn_handle});
925 }
926
ClearL2capAcceptlist(uint16_t conn_handle,uint16_t local_cid,uint16_t remote_cid)927 void SnoopLogger::ClearL2capAcceptlist(
928 uint16_t conn_handle, uint16_t local_cid, uint16_t remote_cid) {
929 if (btsnoop_mode_ != kBtSnoopLogModeFiltered ||
930 !IsFilterEnabled(kBtSnoopLogFilterProfileRfcommProperty)) {
931 return;
932 }
933
934 LOG_DEBUG(
935 "Clearing acceptlist from l2cap channel. conn_handle=%d local cid=%d remote cid=%d",
936 conn_handle,
937 local_cid,
938 remote_cid);
939 std::lock_guard<std::mutex> lock(filter_tracker_list_mutex);
940
941 filter_tracker_list[conn_handle].RemoveL2capCid(local_cid, remote_cid);
942 }
943
IsA2dpMediaChannel(uint16_t conn_handle,uint16_t cid,bool is_local_cid)944 bool SnoopLogger::IsA2dpMediaChannel(uint16_t conn_handle, uint16_t cid, bool is_local_cid) {
945 if (btsnoop_mode_ != kBtSnoopLogModeFiltered ||
946 !IsFilterEnabled(kBtSnoopLogFilterProfileA2dpProperty)) {
947 return false;
948 }
949
950 std::lock_guard<std::mutex> lock(a2dpMediaChannels_mutex);
951 auto iter = std::find_if(
952 a2dpMediaChannels.begin(),
953 a2dpMediaChannels.end(),
954 [conn_handle, cid, is_local_cid](auto& el) {
955 if (el.conn_handle != conn_handle) return false;
956
957 if (is_local_cid) return el.local_cid == cid;
958
959 return el.remote_cid == cid;
960 });
961
962 return iter != a2dpMediaChannels.end();
963 }
964
IsA2dpMediaPacket(bool is_received,uint8_t * packet)965 bool SnoopLogger::IsA2dpMediaPacket(bool is_received, uint8_t* packet) {
966 uint16_t cid, conn_handle;
967 bool is_local_cid = is_received;
968 /*is_received signifies Rx packet so packet will have local_cid at offset 6
969 * Tx packet with is_received as false and have remote_cid at the offset*/
970
971 conn_handle = (uint16_t)((packet[0] + (packet[1] << 8)) & 0x0FFF);
972 cid = (uint16_t)(packet[6] + (packet[7] << 8));
973
974 return IsA2dpMediaChannel(conn_handle, cid, is_local_cid);
975 }
976
AddA2dpMediaChannel(uint16_t conn_handle,uint16_t local_cid,uint16_t remote_cid)977 void SnoopLogger::AddA2dpMediaChannel(
978 uint16_t conn_handle, uint16_t local_cid, uint16_t remote_cid) {
979 if (btsnoop_mode_ != kBtSnoopLogModeFiltered ||
980 !IsFilterEnabled(kBtSnoopLogFilterProfileA2dpProperty)) {
981 return;
982 }
983
984 if (!SnoopLogger::IsA2dpMediaChannel(conn_handle, local_cid, true)) {
985 LOG_INFO(
986 "Add A2DP media channel filtering. conn_handle=%d local cid=%d remote cid=%d",
987 conn_handle,
988 local_cid,
989 remote_cid);
990 std::lock_guard<std::mutex> lock(a2dpMediaChannels_mutex);
991 a2dpMediaChannels.push_back({conn_handle, local_cid, remote_cid});
992 }
993 }
994
RemoveA2dpMediaChannel(uint16_t conn_handle,uint16_t local_cid)995 void SnoopLogger::RemoveA2dpMediaChannel(uint16_t conn_handle, uint16_t local_cid) {
996 if (btsnoop_mode_ != kBtSnoopLogModeFiltered ||
997 !IsFilterEnabled(kBtSnoopLogFilterProfileA2dpProperty)) {
998 return;
999 }
1000
1001 std::lock_guard<std::mutex> lock(a2dpMediaChannels_mutex);
1002 a2dpMediaChannels.erase(
1003 std::remove_if(
1004 a2dpMediaChannels.begin(),
1005 a2dpMediaChannels.end(),
1006 [conn_handle, local_cid](auto& el) {
1007 return (el.conn_handle == conn_handle && el.local_cid == local_cid);
1008 }),
1009 a2dpMediaChannels.end());
1010 }
1011
SetRfcommPortOpen(uint16_t conn_handle,uint16_t local_cid,uint8_t dlci,uint16_t uuid,bool flow)1012 void SnoopLogger::SetRfcommPortOpen(
1013 uint16_t conn_handle, uint16_t local_cid, uint8_t dlci, uint16_t uuid, bool flow) {
1014 if (btsnoop_mode_ != kBtSnoopLogModeFiltered ||
1015 (!IsFilterEnabled(kBtSnoopLogFilterProfilePbapModeProperty) &&
1016 !IsFilterEnabled(kBtSnoopLogFilterProfileMapModeProperty))) {
1017 return;
1018 }
1019
1020 std::lock_guard<std::mutex> lock(profiles_filter_mutex);
1021
1022 profile_type_t profile = FILTER_PROFILE_NONE;
1023 auto& filters = profiles_filter_table[conn_handle];
1024 {
1025 filters.SetupProfilesFilter(
1026 IsFilterEnabled(kBtSnoopLogFilterProfilePbapModeProperty),
1027 IsFilterEnabled(kBtSnoopLogFilterProfileMapModeProperty));
1028 }
1029
1030 LOG_INFO(
1031 "RFCOMM port is opened: handle=%d(0x%x),"
1032 " lcid=%d(0x%x), dlci=%d(0x%x), uuid=%d(0x%x)%s",
1033 conn_handle,
1034 conn_handle,
1035 local_cid,
1036 local_cid,
1037 dlci,
1038 dlci,
1039 uuid,
1040 uuid,
1041 flow ? " Credit Based Flow Control enabled" : "");
1042
1043 if (uuid == PROFILE_UUID_PBAP || (dlci >> 1) == PROFILE_SCN_PBAP) {
1044 profile = FILTER_PROFILE_PBAP;
1045 } else if (uuid == PROFILE_UUID_MAP || (dlci >> 1) == PROFILE_SCN_MAP) {
1046 profile = FILTER_PROFILE_MAP;
1047 } else if (uuid == PROFILE_UUID_HFP_HS) {
1048 profile = FILTER_PROFILE_HFP_HS;
1049 } else if (uuid == PROFILE_UUID_HFP_HF) {
1050 profile = FILTER_PROFILE_HFP_HF;
1051 }
1052
1053 if (profile >= 0) {
1054 filters.ProfileRfcommOpen(profile, local_cid, dlci, uuid, flow);
1055 }
1056 }
1057
SetRfcommPortClose(uint16_t handle,uint16_t local_cid,uint8_t dlci,uint16_t uuid)1058 void SnoopLogger::SetRfcommPortClose(
1059 uint16_t handle, uint16_t local_cid, uint8_t dlci, uint16_t uuid) {
1060 if (btsnoop_mode_ != kBtSnoopLogModeFiltered ||
1061 (!IsFilterEnabled(kBtSnoopLogFilterProfilePbapModeProperty) &&
1062 !IsFilterEnabled(kBtSnoopLogFilterProfileMapModeProperty))) {
1063 return;
1064 }
1065
1066 std::lock_guard<std::mutex> lock(profiles_filter_mutex);
1067
1068 auto& filters = profiles_filter_table[handle];
1069 LOG_INFO(
1070 "RFCOMM port is closed: handle=%d(0x%x),"
1071 " lcid=%d(0x%x), dlci=%d(0x%x), uuid=%d(0x%x)",
1072 handle,
1073 handle,
1074 local_cid,
1075 local_cid,
1076 dlci,
1077 dlci,
1078 uuid,
1079 uuid);
1080
1081 filters.ProfileRfcommClose(filters.DlciToProfile(true, local_cid, dlci));
1082 }
1083
SetL2capChannelOpen(uint16_t handle,uint16_t local_cid,uint16_t remote_cid,uint16_t psm,bool flow)1084 void SnoopLogger::SetL2capChannelOpen(
1085 uint16_t handle, uint16_t local_cid, uint16_t remote_cid, uint16_t psm, bool flow) {
1086 if (btsnoop_mode_ != kBtSnoopLogModeFiltered ||
1087 (!IsFilterEnabled(kBtSnoopLogFilterProfilePbapModeProperty) &&
1088 !IsFilterEnabled(kBtSnoopLogFilterProfileMapModeProperty))) {
1089 return;
1090 }
1091
1092 std::lock_guard<std::mutex> lock(profiles_filter_mutex);
1093 profile_type_t profile = FILTER_PROFILE_NONE;
1094 auto& filters = profiles_filter_table[handle];
1095 {
1096 filters.SetupProfilesFilter(
1097 IsFilterEnabled(kBtSnoopLogFilterProfilePbapModeProperty),
1098 IsFilterEnabled(kBtSnoopLogFilterProfileMapModeProperty));
1099 }
1100
1101 LOG_INFO(
1102 "L2CAP channel is opened: handle=%d(0x%x), lcid=%d(0x%x),"
1103 " rcid=%d(0x%x), psm=0x%x%s",
1104 handle,
1105 handle,
1106 local_cid,
1107 local_cid,
1108 remote_cid,
1109 remote_cid,
1110 psm,
1111 flow ? " Standard or Enhanced Control enabled" : "");
1112
1113 if (psm == PROFILE_PSM_RFCOMM) {
1114 filters.ch_rfc_l = local_cid;
1115 filters.ch_rfc_r = remote_cid;
1116 } else if (psm == PROFILE_PSM_PBAP) {
1117 profile = FILTER_PROFILE_PBAP;
1118 } else if (psm == PROFILE_PSM_MAP) {
1119 profile = FILTER_PROFILE_MAP;
1120 }
1121
1122 if (profile >= 0) {
1123 filters.ProfileL2capOpen(profile, local_cid, remote_cid, psm, flow);
1124 }
1125 }
1126
SetL2capChannelClose(uint16_t handle,uint16_t local_cid,uint16_t remote_cid)1127 void SnoopLogger::SetL2capChannelClose(uint16_t handle, uint16_t local_cid, uint16_t remote_cid) {
1128 if (btsnoop_mode_ != kBtSnoopLogModeFiltered ||
1129 (!IsFilterEnabled(kBtSnoopLogFilterProfilePbapModeProperty) &&
1130 !IsFilterEnabled(kBtSnoopLogFilterProfileMapModeProperty))) {
1131 return;
1132 }
1133
1134 std::lock_guard<std::mutex> lock(profiles_filter_mutex);
1135
1136 auto& filters = profiles_filter_table[handle];
1137
1138 LOG_INFO(
1139 "L2CAP channel is closed: handle=%d(0x%x), lcid=%d(0x%x),"
1140 " rcid=%d(0x%x)",
1141 handle,
1142 handle,
1143 local_cid,
1144 local_cid,
1145 remote_cid,
1146 remote_cid);
1147
1148 filters.ProfileL2capClose(filters.CidToProfile(true, local_cid));
1149 }
1150
FilterCapturedPacket(HciPacket & packet,Direction direction,PacketType type,uint32_t & length,PacketHeaderType header)1151 void SnoopLogger::FilterCapturedPacket(
1152 HciPacket& packet,
1153 Direction direction,
1154 PacketType type,
1155 uint32_t& length,
1156 PacketHeaderType header) {
1157 if (btsnoop_mode_ != kBtSnoopLogModeFiltered || type != PacketType::ACL) {
1158 return;
1159 }
1160
1161 if (IsFilterEnabled(kBtSnoopLogFilterProfileA2dpProperty)) {
1162 if (IsA2dpMediaPacket(direction == Direction::INCOMING, (uint8_t*)packet.data())) {
1163 length = 0;
1164 return;
1165 }
1166 }
1167
1168 if (IsFilterEnabled(kBtSnoopLogFilterHeadersProperty)) {
1169 CalculateAclPacketLength(length, (uint8_t*)packet.data(), direction == Direction::INCOMING);
1170 }
1171
1172 if (IsFilterEnabled(kBtSnoopLogFilterProfilePbapModeProperty) ||
1173 IsFilterEnabled(kBtSnoopLogFilterProfileMapModeProperty)) {
1174 // If HeadersFiltered applied, do not use ProfilesFiltered
1175 if (length == ntohl(header.length_original)) {
1176 if (packet.size() + EXTRA_BUF_SIZE > DEFAULT_PACKET_SIZE) {
1177 // Add additional bytes for magic string in case
1178 // payload length is less than the length of magic string.
1179 packet.resize((size_t)(packet.size() + EXTRA_BUF_SIZE));
1180 }
1181
1182 length = FilterProfiles(direction == Direction::INCOMING, (uint8_t*)packet.data());
1183 if (length == 0) return;
1184 }
1185 }
1186
1187 if (IsFilterEnabled(kBtSnoopLogFilterProfileRfcommProperty)) {
1188 bool shouldFilter =
1189 SnoopLogger::ShouldFilterLog(direction == Direction::INCOMING, (uint8_t*)packet.data());
1190 if (shouldFilter) {
1191 length = L2CAP_HEADER_SIZE + PACKET_TYPE_LENGTH;
1192 }
1193 }
1194 }
1195
Capture(HciPacket & packet,Direction direction,PacketType type)1196 void SnoopLogger::Capture(HciPacket& packet, Direction direction, PacketType type) {
1197 uint64_t timestamp_us =
1198 std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch())
1199 .count();
1200 std::bitset<32> flags = 0;
1201 switch (type) {
1202 case PacketType::CMD:
1203 flags.set(0, false);
1204 flags.set(1, true);
1205 break;
1206 case PacketType::ACL:
1207 case PacketType::ISO:
1208 case PacketType::SCO:
1209 flags.set(0, direction == Direction::INCOMING);
1210 flags.set(1, false);
1211 break;
1212 case PacketType::EVT:
1213 flags.set(0, true);
1214 flags.set(1, true);
1215 break;
1216 }
1217 uint32_t length = packet.size() + /* type byte */ PACKET_TYPE_LENGTH;
1218 PacketHeaderType header = {.length_original = htonl(length),
1219 .length_captured = htonl(length),
1220 .flags = htonl(static_cast<uint32_t>(flags.to_ulong())),
1221 .dropped_packets = 0,
1222 .timestamp = htonll(timestamp_us + kBtSnoopEpochDelta),
1223 .type = static_cast<uint8_t>(type)};
1224 {
1225 std::lock_guard<std::recursive_mutex> lock(file_mutex_);
1226 if (btsnoop_mode_ == kBtSnoopLogModeDisabled) {
1227 // btsnoop disabled, log in-memory btsnooz log only
1228 std::stringstream ss;
1229 size_t included_length = get_btsnooz_packet_length_to_write(packet, type, qualcomm_debug_log_enabled_);
1230 header.length_captured = htonl(included_length + /* type byte */ PACKET_TYPE_LENGTH);
1231 if (!ss.write(reinterpret_cast<const char*>(&header), sizeof(PacketHeaderType))) {
1232 LOG_ERROR("Failed to write packet header for btsnooz, error: \"%s\"", strerror(errno));
1233 }
1234 if (!ss.write(reinterpret_cast<const char*>(packet.data()), included_length)) {
1235 LOG_ERROR("Failed to write packet payload for btsnooz, error: \"%s\"", strerror(errno));
1236 }
1237 btsnooz_buffer_.Push(ss.str());
1238 return;
1239 }
1240
1241 FilterCapturedPacket(packet, direction, type, length, header);
1242
1243 if (length == 0) {
1244 return;
1245 } else if (length != ntohl(header.length_original)) {
1246 header.length_captured = htonl(length);
1247 }
1248
1249 packet_counter_++;
1250 if (packet_counter_ > max_packets_per_file_) {
1251 OpenNextSnoopLogFile();
1252 }
1253 if (!btsnoop_ostream_.write(reinterpret_cast<const char*>(&header), sizeof(PacketHeaderType))) {
1254 LOG_ERROR("Failed to write packet header for btsnoop, error: \"%s\"", strerror(errno));
1255 }
1256 if (!btsnoop_ostream_.write(reinterpret_cast<const char*>(packet.data()), length - 1)) {
1257 LOG_ERROR("Failed to write packet payload for btsnoop, error: \"%s\"", strerror(errno));
1258 }
1259
1260 if (socket_ != nullptr) {
1261 socket_->Write(&header, sizeof(PacketHeaderType));
1262 socket_->Write(packet.data(), packet.size());
1263 }
1264
1265 // std::ofstream::flush() pushes user data into kernel memory. The data will be written even if this process
1266 // crashes. However, data will be lost if there is a kernel panic, which is out of scope of BT snoop log.
1267 // NOTE: std::ofstream::write() followed by std::ofstream::flush() has similar effect as UNIX write(fd, data, len)
1268 // as write() syscall dumps data into kernel memory directly
1269 if (!btsnoop_ostream_.flush()) {
1270 LOG_ERROR("Failed to flush, error: \"%s\"", strerror(errno));
1271 }
1272 }
1273 }
1274
DumpSnoozLogToFile(const std::vector<std::string> & data) const1275 void SnoopLogger::DumpSnoozLogToFile(const std::vector<std::string>& data) const {
1276 std::lock_guard<std::recursive_mutex> lock(file_mutex_);
1277 if (btsnoop_mode_ != kBtSnoopLogModeDisabled) {
1278 LOG_DEBUG("btsnoop log is enabled, skip dumping btsnooz log");
1279 return;
1280 }
1281
1282 auto last_file_path = get_last_log_path(snooz_log_path_);
1283
1284 if (os::FileExists(snooz_log_path_)) {
1285 if (!os::RenameFile(snooz_log_path_, last_file_path)) {
1286 LOG_ERROR(
1287 "Unabled to rename existing snooz log from \"%s\" to \"%s\"",
1288 snooz_log_path_.c_str(),
1289 last_file_path.c_str());
1290 }
1291 } else {
1292 LOG_INFO("Previous log file \"%s\" does not exist, skip renaming", snooz_log_path_.c_str());
1293 }
1294
1295 mode_t prevmask = umask(0);
1296 // do not use std::ios::app as we want override the existing file
1297 std::ofstream btsnooz_ostream(snooz_log_path_, std::ios::binary | std::ios::out);
1298 if (!btsnooz_ostream.good()) {
1299 LOG_ALWAYS_FATAL("Unable to open snoop log at \"%s\", error: \"%s\"", snooz_log_path_.c_str(), strerror(errno));
1300 }
1301 umask(prevmask);
1302 if (!btsnooz_ostream.write(
1303 reinterpret_cast<const char*>(&SnoopLoggerCommon::kBtSnoopFileHeader),
1304 sizeof(SnoopLoggerCommon::FileHeaderType))) {
1305 LOG_ALWAYS_FATAL("Unable to write file header to \"%s\", error: \"%s\"", snooz_log_path_.c_str(), strerror(errno));
1306 }
1307 for (const auto& packet : data) {
1308 if (!btsnooz_ostream.write(packet.data(), packet.size())) {
1309 LOG_ERROR("Failed to write packet payload for btsnooz, error: \"%s\"", strerror(errno));
1310 }
1311 }
1312 if (!btsnooz_ostream.flush()) {
1313 LOG_ERROR("Failed to flush, error: \"%s\"", strerror(errno));
1314 }
1315 }
1316
ListDependencies(ModuleList * list) const1317 void SnoopLogger::ListDependencies(ModuleList* list) const {
1318 // We have no dependencies
1319 }
1320
Start()1321 void SnoopLogger::Start() {
1322 std::lock_guard<std::recursive_mutex> lock(file_mutex_);
1323 if (btsnoop_mode_ != kBtSnoopLogModeDisabled) {
1324 OpenNextSnoopLogFile();
1325
1326 if (btsnoop_mode_ == kBtSnoopLogModeFiltered) {
1327 EnableFilters();
1328 }
1329
1330 if (bluetooth::common::InitFlags::IsSnoopLoggerSocketEnabled()) {
1331 auto snoop_logger_socket = std::make_unique<SnoopLoggerSocket>(&syscall_if);
1332 snoop_logger_socket_thread_ = std::make_unique<SnoopLoggerSocketThread>(std::move(snoop_logger_socket));
1333 auto thread_started_future = snoop_logger_socket_thread_->Start();
1334 thread_started_future.wait();
1335 if (thread_started_future.get()) {
1336 RegisterSocket(snoop_logger_socket_thread_.get());
1337 } else {
1338 snoop_logger_socket_thread_->Stop();
1339 snoop_logger_socket_thread_.reset();
1340 snoop_logger_socket_thread_ = nullptr;
1341 }
1342 }
1343 }
1344 alarm_ = std::make_unique<os::RepeatingAlarm>(GetHandler());
1345 alarm_->Schedule(
1346 common::Bind(&delete_old_btsnooz_files, snooz_log_path_, snooz_log_life_time_), snooz_log_delete_alarm_interval_);
1347 }
1348
Stop()1349 void SnoopLogger::Stop() {
1350 std::lock_guard<std::recursive_mutex> lock(file_mutex_);
1351 LOG_DEBUG("Closing btsnoop log data at %s", snoop_log_path_.c_str());
1352 CloseCurrentSnoopLogFile();
1353
1354 if (snoop_logger_socket_thread_ != nullptr) {
1355 snoop_logger_socket_thread_->Stop();
1356 snoop_logger_socket_thread_.reset();
1357 snoop_logger_socket_thread_ = nullptr;
1358 socket_ = nullptr;
1359 }
1360
1361 btsnoop_mode_.clear();
1362 // Disable all filters
1363 DisableFilters();
1364
1365 // Cancel the alarm
1366 alarm_->Cancel();
1367 alarm_.reset();
1368 // delete any existing snooz logs
1369 if (!snoop_log_persists) {
1370 delete_btsnoop_files(snooz_log_path_);
1371 }
1372 }
1373
GetDumpsysData(flatbuffers::FlatBufferBuilder * builder) const1374 DumpsysDataFinisher SnoopLogger::GetDumpsysData(flatbuffers::FlatBufferBuilder* builder) const {
1375 LOG_DEBUG("Dumping btsnooz log data to %s", snooz_log_path_.c_str());
1376 DumpSnoozLogToFile(btsnooz_buffer_.Pull());
1377 return Module::GetDumpsysData(builder);
1378 }
1379
GetMaxPacketsPerFile()1380 size_t SnoopLogger::GetMaxPacketsPerFile() {
1381 // Allow override max packet per file via system property
1382 auto max_packets_per_file = kDefaultBtSnoopMaxPacketsPerFile;
1383 {
1384 auto max_packets_per_file_prop = os::GetSystemProperty(kBtSnoopMaxPacketsPerFileProperty);
1385 if (max_packets_per_file_prop) {
1386 auto max_packets_per_file_number = common::Uint64FromString(max_packets_per_file_prop.value());
1387 if (max_packets_per_file_number) {
1388 max_packets_per_file = max_packets_per_file_number.value();
1389 }
1390 }
1391 }
1392 return max_packets_per_file;
1393 }
1394
GetMaxPacketsPerBuffer()1395 size_t SnoopLogger::GetMaxPacketsPerBuffer() {
1396 // We want to use at most 256 KB memory for btsnooz log for release builds
1397 // and 512 KB memory for userdebug/eng builds
1398 auto is_debuggable = os::GetSystemPropertyBool(kIsDebuggableProperty, false);
1399
1400 size_t btsnooz_max_memory_usage_bytes = (is_debuggable ? 1024 : 256) * 1024;
1401 // Calculate max number of packets based on max memory usage and max packet size
1402 return btsnooz_max_memory_usage_bytes / kDefaultBtSnoozMaxBytesPerPacket;
1403 }
1404
GetBtSnoopMode()1405 std::string SnoopLogger::GetBtSnoopMode() {
1406 // Default mode is FILTERED on userdebug/eng build, DISABLED on user build.
1407 // In userdebug/eng build, it can also be overwritten by modifying the global setting
1408 std::string default_mode = kBtSnoopLogModeDisabled;
1409 {
1410 auto is_debuggable = os::GetSystemPropertyBool(kIsDebuggableProperty, false);
1411 if (is_debuggable) {
1412 auto default_mode_property = os::GetSystemProperty(kBtSnoopDefaultLogModeProperty);
1413 if (default_mode_property) {
1414 default_mode = std::move(default_mode_property.value());
1415 } else {
1416 default_mode = kBtSnoopLogModeFiltered;
1417 }
1418 }
1419 }
1420
1421 // Get the actual mode if exist
1422 std::string btsnoop_mode = default_mode;
1423 {
1424 auto btsnoop_mode_prop = os::GetSystemProperty(kBtSnoopLogModeProperty);
1425 if (btsnoop_mode_prop) {
1426 btsnoop_mode = std::move(btsnoop_mode_prop.value());
1427 }
1428 }
1429
1430 // If Snoop Logger already set up, return current mode
1431 bool btsnoop_mode_empty = btsnoop_mode_.empty();
1432 LOG_INFO("btsnoop_mode_empty: %d", btsnoop_mode_empty);
1433 if (!btsnoop_mode_empty) {
1434 return btsnoop_mode_;
1435 }
1436
1437 return btsnoop_mode;
1438 }
1439
RegisterSocket(SnoopLoggerSocketInterface * socket)1440 void SnoopLogger::RegisterSocket(SnoopLoggerSocketInterface* socket) {
1441 std::lock_guard<std::recursive_mutex> lock(file_mutex_);
1442 socket_ = socket;
1443 }
1444
IsBtSnoopLogPersisted()1445 bool SnoopLogger::IsBtSnoopLogPersisted() {
1446 auto is_debuggable = os::GetSystemPropertyBool(kIsDebuggableProperty, false);
1447 return is_debuggable && os::GetSystemPropertyBool(kBtSnoopLogPersists, false);
1448 }
1449
IsQualcommDebugLogEnabled()1450 bool SnoopLogger::IsQualcommDebugLogEnabled() {
1451 // Check system prop if the soc manufacturer is Qualcomm
1452 bool qualcomm_debug_log_enabled = false;
1453 {
1454 auto soc_manufacturer_prop = os::GetSystemProperty(kSoCManufacturerProperty);
1455 qualcomm_debug_log_enabled = soc_manufacturer_prop.has_value() &&
1456 common::StringTrim(soc_manufacturer_prop.value()) == kSoCManufacturerQualcomm;
1457 }
1458 return qualcomm_debug_log_enabled;
1459 }
1460
__anonfecfc9330502() 1461 const ModuleFactory SnoopLogger::Factory = ModuleFactory([]() {
1462 return new SnoopLogger(
1463 os::ParameterProvider::SnoopLogFilePath(),
1464 os::ParameterProvider::SnoozLogFilePath(),
1465 GetMaxPacketsPerFile(),
1466 GetMaxPacketsPerBuffer(),
1467 GetBtSnoopMode(),
1468 IsQualcommDebugLogEnabled(),
1469 kBtSnoozLogLifeTime,
1470 kBtSnoozLogDeleteRepeatingAlarmInterval,
1471 IsBtSnoopLogPersisted());
1472 });
1473
1474 } // namespace hal
1475 } // namespace bluetooth
1476