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 <base/logging.h>
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <statslog.h>
21 #include <stdio.h>
22 #include <sys/stat.h>
23
24 #include "btif_bqr.h"
25 #include "btif_dm.h"
26 #include "common/leaky_bonded_queue.h"
27 #include "osi/include/properties.h"
28 #include "stack/btm/btm_int.h"
29
30 namespace bluetooth {
31 namespace bqr {
32
33 using bluetooth::common::LeakyBondedQueue;
34 using std::chrono::system_clock;
35
36 // The instance of BQR event queue
37 static std::unique_ptr<LeakyBondedQueue<BqrVseSubEvt>> kpBqrEventQueue(
38 new LeakyBondedQueue<BqrVseSubEvt>(kBqrEventQueueSize));
39
ParseBqrLinkQualityEvt(uint8_t length,uint8_t * p_param_buf)40 void BqrVseSubEvt::ParseBqrLinkQualityEvt(uint8_t length,
41 uint8_t* p_param_buf) {
42 if (length < kLinkQualityParamTotalLen) {
43 LOG(FATAL) << __func__
44 << ": Parameter total length: " << std::to_string(length)
45 << " is abnormal. It shall be not shorter than: "
46 << std::to_string(kLinkQualityParamTotalLen);
47 return;
48 }
49
50 STREAM_TO_UINT8(bqr_link_quality_event_.quality_report_id, p_param_buf);
51 STREAM_TO_UINT8(bqr_link_quality_event_.packet_types, p_param_buf);
52 STREAM_TO_UINT16(bqr_link_quality_event_.connection_handle, p_param_buf);
53 STREAM_TO_UINT8(bqr_link_quality_event_.connection_role, p_param_buf);
54 STREAM_TO_INT8(bqr_link_quality_event_.tx_power_level, p_param_buf);
55 STREAM_TO_INT8(bqr_link_quality_event_.rssi, p_param_buf);
56 STREAM_TO_UINT8(bqr_link_quality_event_.snr, p_param_buf);
57 STREAM_TO_UINT8(bqr_link_quality_event_.unused_afh_channel_count,
58 p_param_buf);
59 STREAM_TO_UINT8(bqr_link_quality_event_.afh_select_unideal_channel_count,
60 p_param_buf);
61 STREAM_TO_UINT16(bqr_link_quality_event_.lsto, p_param_buf);
62 STREAM_TO_UINT32(bqr_link_quality_event_.connection_piconet_clock,
63 p_param_buf);
64 STREAM_TO_UINT32(bqr_link_quality_event_.retransmission_count, p_param_buf);
65 STREAM_TO_UINT32(bqr_link_quality_event_.no_rx_count, p_param_buf);
66 STREAM_TO_UINT32(bqr_link_quality_event_.nak_count, p_param_buf);
67 STREAM_TO_UINT32(bqr_link_quality_event_.last_tx_ack_timestamp, p_param_buf);
68 STREAM_TO_UINT32(bqr_link_quality_event_.flow_off_count, p_param_buf);
69 STREAM_TO_UINT32(bqr_link_quality_event_.last_flow_on_timestamp, p_param_buf);
70 STREAM_TO_UINT32(bqr_link_quality_event_.buffer_overflow_bytes, p_param_buf);
71 STREAM_TO_UINT32(bqr_link_quality_event_.buffer_underflow_bytes, p_param_buf);
72
73 const auto now = system_clock::to_time_t(system_clock::now());
74 localtime_r(&now, &tm_timestamp_);
75 }
76
WriteLmpLlTraceLogFile(int fd,uint8_t length,uint8_t * p_param_buf)77 void BqrVseSubEvt::WriteLmpLlTraceLogFile(int fd, uint8_t length,
78 uint8_t* p_param_buf) {
79 const auto now = system_clock::to_time_t(system_clock::now());
80 localtime_r(&now, &tm_timestamp_);
81
82 STREAM_TO_UINT8(bqr_log_dump_event_.quality_report_id, p_param_buf);
83 STREAM_TO_UINT16(bqr_log_dump_event_.connection_handle, p_param_buf);
84 length -= kLogDumpParamTotalLen;
85 bqr_log_dump_event_.vendor_specific_parameter = p_param_buf;
86
87 std::stringstream ss_log;
88 ss_log << "\n"
89 << std::put_time(&tm_timestamp_, "%m-%d %H:%M:%S ")
90 << "Handle: " << loghex(bqr_log_dump_event_.connection_handle)
91 << " VSP: ";
92
93 TEMP_FAILURE_RETRY(write(fd, ss_log.str().c_str(), ss_log.str().size()));
94 TEMP_FAILURE_RETRY(
95 write(fd, bqr_log_dump_event_.vendor_specific_parameter, length));
96 LmpLlMessageTraceCounter++;
97 }
98
WriteBtSchedulingTraceLogFile(int fd,uint8_t length,uint8_t * p_param_buf)99 void BqrVseSubEvt::WriteBtSchedulingTraceLogFile(int fd, uint8_t length,
100 uint8_t* p_param_buf) {
101 const auto now = system_clock::to_time_t(system_clock::now());
102 localtime_r(&now, &tm_timestamp_);
103
104 STREAM_TO_UINT8(bqr_log_dump_event_.quality_report_id, p_param_buf);
105 STREAM_TO_UINT16(bqr_log_dump_event_.connection_handle, p_param_buf);
106 length -= kLogDumpParamTotalLen;
107 bqr_log_dump_event_.vendor_specific_parameter = p_param_buf;
108
109 std::stringstream ss_log;
110 ss_log << "\n"
111 << std::put_time(&tm_timestamp_, "%m-%d %H:%M:%S ")
112 << "Handle: " << loghex(bqr_log_dump_event_.connection_handle)
113 << " VSP: ";
114
115 TEMP_FAILURE_RETRY(write(fd, ss_log.str().c_str(), ss_log.str().size()));
116 TEMP_FAILURE_RETRY(
117 write(fd, bqr_log_dump_event_.vendor_specific_parameter, length));
118 BtSchedulingTraceCounter++;
119 }
120
ToString() const121 std::string BqrVseSubEvt::ToString() const {
122 std::stringstream ss;
123 ss << QualityReportIdToString(bqr_link_quality_event_.quality_report_id)
124 << ", Handle: " << loghex(bqr_link_quality_event_.connection_handle)
125 << ", " << PacketTypeToString(bqr_link_quality_event_.packet_types) << ", "
126 << ((bqr_link_quality_event_.connection_role == 0) ? "Master" : "Slave ")
127 << ", PwLv: " << std::to_string(bqr_link_quality_event_.tx_power_level)
128 << ", RSSI: " << std::to_string(bqr_link_quality_event_.rssi)
129 << ", SNR: " << std::to_string(bqr_link_quality_event_.snr)
130 << ", UnusedCh: "
131 << std::to_string(bqr_link_quality_event_.unused_afh_channel_count)
132 << ", UnidealCh: "
133 << std::to_string(bqr_link_quality_event_.afh_select_unideal_channel_count)
134 << ", ReTx: "
135 << std::to_string(bqr_link_quality_event_.retransmission_count)
136 << ", NoRX: " << std::to_string(bqr_link_quality_event_.no_rx_count)
137 << ", NAK: " << std::to_string(bqr_link_quality_event_.nak_count)
138 << ", FlowOff: " << std::to_string(bqr_link_quality_event_.flow_off_count)
139 << ", OverFlow: "
140 << std::to_string(bqr_link_quality_event_.buffer_overflow_bytes)
141 << ", UndFlow: "
142 << std::to_string(bqr_link_quality_event_.buffer_underflow_bytes);
143 return ss.str();
144 }
145
QualityReportIdToString(uint8_t quality_report_id)146 std::string QualityReportIdToString(uint8_t quality_report_id) {
147 switch (quality_report_id) {
148 case QUALITY_REPORT_ID_MONITOR_MODE:
149 return "Monitoring ";
150 case QUALITY_REPORT_ID_APPROACH_LSTO:
151 return "Appro LSTO ";
152 case QUALITY_REPORT_ID_A2DP_AUDIO_CHOPPY:
153 return "A2DP Choppy";
154 case QUALITY_REPORT_ID_SCO_VOICE_CHOPPY:
155 return "SCO Choppy ";
156 default:
157 return "Invalid ";
158 }
159 }
160
PacketTypeToString(uint8_t packet_type)161 std::string PacketTypeToString(uint8_t packet_type) {
162 switch (packet_type) {
163 case PACKET_TYPE_ID:
164 return "ID";
165 case PACKET_TYPE_NULL:
166 return "NULL";
167 case PACKET_TYPE_POLL:
168 return "POLL";
169 case PACKET_TYPE_FHS:
170 return "FHS";
171 case PACKET_TYPE_HV1:
172 return "HV1";
173 case PACKET_TYPE_HV2:
174 return "HV2";
175 case PACKET_TYPE_HV3:
176 return "HV3";
177 case PACKET_TYPE_DV:
178 return "DV";
179 case PACKET_TYPE_EV3:
180 return "EV3";
181 case PACKET_TYPE_EV4:
182 return "EV4";
183 case PACKET_TYPE_EV5:
184 return "EV5";
185 case PACKET_TYPE_2EV3:
186 return "2EV3";
187 case PACKET_TYPE_2EV5:
188 return "2EV5";
189 case PACKET_TYPE_3EV3:
190 return "3EV3";
191 case PACKET_TYPE_3EV5:
192 return "3EV5";
193 case PACKET_TYPE_DM1:
194 return "DM1";
195 case PACKET_TYPE_DH1:
196 return "DH1";
197 case PACKET_TYPE_DM3:
198 return "DM3";
199 case PACKET_TYPE_DH3:
200 return "DH3";
201 case PACKET_TYPE_DM5:
202 return "DM5";
203 case PACKET_TYPE_DH5:
204 return "DH5";
205 case PACKET_TYPE_AUX1:
206 return "AUX1";
207 case PACKET_TYPE_2DH1:
208 return "2DH1";
209 case PACKET_TYPE_2DH3:
210 return "2DH3";
211 case PACKET_TYPE_2DH5:
212 return "2DH5";
213 case PACKET_TYPE_3DH1:
214 return "3DH1";
215 case PACKET_TYPE_3DH3:
216 return "3DH3";
217 case PACKET_TYPE_3DH5:
218 return "3DH5";
219 default:
220 return "UnKnown ";
221 }
222 }
223
EnableBtQualityReport(bool is_enable)224 void EnableBtQualityReport(bool is_enable) {
225 LOG(INFO) << __func__ << ": is_enable: " << logbool(is_enable);
226
227 char bqr_prop_evtmask[PROPERTY_VALUE_MAX] = {0};
228 char bqr_prop_interval_ms[PROPERTY_VALUE_MAX] = {0};
229 osi_property_get(kpPropertyEventMask, bqr_prop_evtmask, "");
230 osi_property_get(kpPropertyMinReportIntervalMs, bqr_prop_interval_ms, "");
231
232 if (strlen(bqr_prop_evtmask) == 0 || strlen(bqr_prop_interval_ms) == 0) {
233 LOG(WARNING) << __func__ << ": Bluetooth Quality Report is disabled."
234 << " bqr_prop_evtmask: " << bqr_prop_evtmask
235 << ", bqr_prop_interval_ms: " << bqr_prop_interval_ms;
236 return;
237 }
238
239 BqrConfiguration bqr_config = {};
240
241 if (is_enable) {
242 bqr_config.report_action = REPORT_ACTION_ADD;
243 bqr_config.quality_event_mask =
244 static_cast<uint32_t>(atoi(bqr_prop_evtmask));
245 bqr_config.minimum_report_interval_ms =
246 static_cast<uint16_t>(atoi(bqr_prop_interval_ms));
247 } else {
248 bqr_config.report_action = REPORT_ACTION_CLEAR;
249 bqr_config.quality_event_mask = kQualityEventMaskAllOff;
250 bqr_config.minimum_report_interval_ms = kMinReportIntervalNoLimit;
251 }
252
253 LOG(INFO) << __func__
254 << ": Event Mask: " << loghex(bqr_config.quality_event_mask)
255 << ", Interval: " << bqr_config.minimum_report_interval_ms;
256 ConfigureBqr(bqr_config);
257 }
258
ConfigureBqr(const BqrConfiguration & bqr_config)259 void ConfigureBqr(const BqrConfiguration& bqr_config) {
260 if (bqr_config.report_action > REPORT_ACTION_CLEAR ||
261 bqr_config.quality_event_mask > kQualityEventMaskAll ||
262 bqr_config.minimum_report_interval_ms > kMinReportIntervalMaxMs) {
263 LOG(FATAL) << __func__ << ": Invalid Parameter"
264 << ", Action: " << bqr_config.report_action
265 << ", Mask: " << loghex(bqr_config.quality_event_mask)
266 << ", Interval: " << bqr_config.minimum_report_interval_ms;
267 return;
268 }
269
270 LOG(INFO) << __func__ << ": Action: " << bqr_config.report_action
271 << ", Mask: " << loghex(bqr_config.quality_event_mask)
272 << ", Interval: " << bqr_config.minimum_report_interval_ms;
273
274 uint8_t param[sizeof(BqrConfiguration)];
275 uint8_t* p_param = param;
276 UINT8_TO_STREAM(p_param, bqr_config.report_action);
277 UINT32_TO_STREAM(p_param, bqr_config.quality_event_mask);
278 UINT16_TO_STREAM(p_param, bqr_config.minimum_report_interval_ms);
279
280 BTM_VendorSpecificCommand(HCI_CONTROLLER_BQR, p_param - param, param,
281 BqrVscCompleteCallback);
282 }
283
BqrVscCompleteCallback(tBTM_VSC_CMPL * p_vsc_cmpl_params)284 void BqrVscCompleteCallback(tBTM_VSC_CMPL* p_vsc_cmpl_params) {
285 if (p_vsc_cmpl_params->param_len < 1) {
286 LOG(ERROR) << __func__ << ": The length of returned parameters is less than 1";
287 return;
288 }
289
290 uint8_t* p_event_param_buf = p_vsc_cmpl_params->p_param_buf;
291 uint8_t status = 0xff;
292 // [Return Parameter] | [Size] | [Purpose]
293 // Status | 1 octet | Command complete status
294 // Current_Quality_Event_Mask | 4 octets | Indicates current bit mask setting
295 STREAM_TO_UINT8(status, p_event_param_buf);
296 if (status != HCI_SUCCESS) {
297 LOG(ERROR) << __func__ << ": Fail to configure BQR. status: " << loghex(status);
298 return;
299 }
300
301 if (p_vsc_cmpl_params->param_len != 5) {
302 LOG(FATAL) << __func__
303 << ": The length of returned parameters is not equal to 5: "
304 << std::to_string(p_vsc_cmpl_params->param_len);
305 return;
306 }
307
308 uint32_t current_quality_event_mask = kQualityEventMaskAllOff;
309 STREAM_TO_UINT32(current_quality_event_mask, p_event_param_buf);
310
311 LOG(INFO) << __func__
312 << ", current event mask: " << loghex(current_quality_event_mask);
313 ConfigureBqrCmpl(current_quality_event_mask);
314 }
315
ConfigureBqrCmpl(uint32_t current_evt_mask)316 void ConfigureBqrCmpl(uint32_t current_evt_mask) {
317 LOG(INFO) << __func__ << ": current_evt_mask: " << loghex(current_evt_mask);
318 // (Un)Register for VSE of Bluetooth Quality Report sub event
319 tBTM_STATUS btm_status = BTM_BT_Quality_Report_VSE_Register(
320 current_evt_mask > kQualityEventMaskAllOff, CategorizeBqrEvent);
321
322 if (btm_status != BTM_SUCCESS) {
323 LOG(ERROR) << __func__ << ": Fail to (un)register VSE of BQR sub event."
324 << " status: " << btm_status;
325 return;
326 }
327
328 if (LmpLlMessageTraceLogFd != INVALID_FD &&
329 (current_evt_mask & kQualityEventMaskLmpMessageTrace) == 0) {
330 LOG(INFO) << __func__ << ": Closing LMP/LL log file.";
331 close(LmpLlMessageTraceLogFd);
332 LmpLlMessageTraceLogFd = INVALID_FD;
333 }
334 if (BtSchedulingTraceLogFd != INVALID_FD &&
335 (current_evt_mask & kQualityEventMaskBtSchedulingTrace) == 0) {
336 LOG(INFO) << __func__ << ": Closing Scheduling log file.";
337 close(BtSchedulingTraceLogFd);
338 BtSchedulingTraceLogFd = INVALID_FD;
339 }
340 }
341
CategorizeBqrEvent(uint8_t length,uint8_t * p_bqr_event)342 void CategorizeBqrEvent(uint8_t length, uint8_t* p_bqr_event) {
343 if (length == 0) {
344 LOG(WARNING) << __func__ << ": Lengths of all of the parameters are zero.";
345 return;
346 }
347
348 uint8_t quality_report_id = p_bqr_event[0];
349 switch (quality_report_id) {
350 case QUALITY_REPORT_ID_MONITOR_MODE:
351 case QUALITY_REPORT_ID_APPROACH_LSTO:
352 case QUALITY_REPORT_ID_A2DP_AUDIO_CHOPPY:
353 case QUALITY_REPORT_ID_SCO_VOICE_CHOPPY:
354 if (length < kLinkQualityParamTotalLen) {
355 LOG(FATAL) << __func__
356 << ": Parameter total length: " << std::to_string(length)
357 << " is abnormal. It shall be not shorter than: "
358 << std::to_string(kLinkQualityParamTotalLen);
359 return;
360 }
361
362 AddLinkQualityEventToQueue(length, p_bqr_event);
363 break;
364
365 // The Root Inflammation and Log Dump related event should be handled and
366 // intercepted already.
367 case QUALITY_REPORT_ID_ROOT_INFLAMMATION:
368 case QUALITY_REPORT_ID_LMP_LL_MESSAGE_TRACE:
369 case QUALITY_REPORT_ID_BT_SCHEDULING_TRACE:
370 case QUALITY_REPORT_ID_CONTROLLER_DBG_INFO:
371 LOG(WARNING) << __func__
372 << ": Unexpected ID: " << loghex(quality_report_id);
373 break;
374
375 default:
376 LOG(WARNING) << __func__ << ": Unknown ID: " << loghex(quality_report_id);
377 break;
378 }
379 }
380
AddLinkQualityEventToQueue(uint8_t length,uint8_t * p_link_quality_event)381 void AddLinkQualityEventToQueue(uint8_t length, uint8_t* p_link_quality_event) {
382 std::unique_ptr<BqrVseSubEvt> p_bqr_event = std::make_unique<BqrVseSubEvt>();
383 p_bqr_event->ParseBqrLinkQualityEvt(length, p_link_quality_event);
384
385 LOG(WARNING) << *p_bqr_event;
386 int ret = android::util::stats_write(
387 android::util::BLUETOOTH_QUALITY_REPORT_REPORTED,
388 p_bqr_event->bqr_link_quality_event_.quality_report_id,
389 p_bqr_event->bqr_link_quality_event_.packet_types,
390 p_bqr_event->bqr_link_quality_event_.connection_handle,
391 p_bqr_event->bqr_link_quality_event_.connection_role,
392 p_bqr_event->bqr_link_quality_event_.tx_power_level,
393 p_bqr_event->bqr_link_quality_event_.rssi,
394 p_bqr_event->bqr_link_quality_event_.snr,
395 p_bqr_event->bqr_link_quality_event_.unused_afh_channel_count,
396 p_bqr_event->bqr_link_quality_event_.afh_select_unideal_channel_count,
397 p_bqr_event->bqr_link_quality_event_.lsto,
398 p_bqr_event->bqr_link_quality_event_.connection_piconet_clock,
399 p_bqr_event->bqr_link_quality_event_.retransmission_count,
400 p_bqr_event->bqr_link_quality_event_.no_rx_count,
401 p_bqr_event->bqr_link_quality_event_.nak_count,
402 p_bqr_event->bqr_link_quality_event_.last_tx_ack_timestamp,
403 p_bqr_event->bqr_link_quality_event_.flow_off_count,
404 p_bqr_event->bqr_link_quality_event_.last_flow_on_timestamp,
405 p_bqr_event->bqr_link_quality_event_.buffer_overflow_bytes,
406 p_bqr_event->bqr_link_quality_event_.buffer_underflow_bytes);
407 if (ret < 0) {
408 LOG(WARNING) << __func__ << ": failed to log BQR event to statsd, error "
409 << ret;
410 }
411 kpBqrEventQueue->Enqueue(p_bqr_event.release());
412 }
413
DumpLmpLlMessage(uint8_t length,uint8_t * p_lmp_ll_message_event)414 void DumpLmpLlMessage(uint8_t length, uint8_t* p_lmp_ll_message_event) {
415 std::unique_ptr<BqrVseSubEvt> p_bqr_event = std::make_unique<BqrVseSubEvt>();
416
417 if (LmpLlMessageTraceLogFd == INVALID_FD ||
418 LmpLlMessageTraceCounter >= kLogDumpEventPerFile) {
419 LmpLlMessageTraceLogFd = OpenLmpLlTraceLogFile();
420 }
421 if (LmpLlMessageTraceLogFd != INVALID_FD) {
422 p_bqr_event->WriteLmpLlTraceLogFile(LmpLlMessageTraceLogFd, length,
423 p_lmp_ll_message_event);
424 }
425 }
426
OpenLmpLlTraceLogFile()427 int OpenLmpLlTraceLogFile() {
428 if (rename(kpLmpLlMessageTraceLogPath, kpLmpLlMessageTraceLastLogPath) != 0 &&
429 errno != ENOENT) {
430 LOG(ERROR) << __func__ << ": Unable to rename '"
431 << kpLmpLlMessageTraceLogPath << "' to '"
432 << kpLmpLlMessageTraceLastLogPath << "' : " << strerror(errno);
433 }
434
435 mode_t prevmask = umask(0);
436 int logfile_fd =
437 open(kpLmpLlMessageTraceLogPath, O_WRONLY | O_CREAT | O_TRUNC,
438 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
439 umask(prevmask);
440 if (logfile_fd == INVALID_FD) {
441 LOG(ERROR) << __func__ << ": Unable to open '" << kpLmpLlMessageTraceLogPath
442 << "' : " << strerror(errno);
443 } else {
444 LmpLlMessageTraceCounter = 0;
445 }
446 return logfile_fd;
447 }
448
DumpBtScheduling(uint8_t length,uint8_t * p_bt_scheduling_event)449 void DumpBtScheduling(uint8_t length, uint8_t* p_bt_scheduling_event) {
450 std::unique_ptr<BqrVseSubEvt> p_bqr_event = std::make_unique<BqrVseSubEvt>();
451
452 if (BtSchedulingTraceLogFd == INVALID_FD ||
453 BtSchedulingTraceCounter == kLogDumpEventPerFile) {
454 BtSchedulingTraceLogFd = OpenBtSchedulingTraceLogFile();
455 }
456 if (BtSchedulingTraceLogFd != INVALID_FD) {
457 p_bqr_event->WriteBtSchedulingTraceLogFile(BtSchedulingTraceLogFd, length,
458 p_bt_scheduling_event);
459 }
460 }
461
OpenBtSchedulingTraceLogFile()462 int OpenBtSchedulingTraceLogFile() {
463 if (rename(kpBtSchedulingTraceLogPath, kpBtSchedulingTraceLastLogPath) != 0 &&
464 errno != ENOENT) {
465 LOG(ERROR) << __func__ << ": Unable to rename '"
466 << kpBtSchedulingTraceLogPath << "' to '"
467 << kpBtSchedulingTraceLastLogPath << "' : " << strerror(errno);
468 }
469
470 mode_t prevmask = umask(0);
471 int logfile_fd =
472 open(kpBtSchedulingTraceLogPath, O_WRONLY | O_CREAT | O_TRUNC,
473 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
474 umask(prevmask);
475 if (logfile_fd == INVALID_FD) {
476 LOG(ERROR) << __func__ << ": Unable to open '" << kpBtSchedulingTraceLogPath
477 << "' : " << strerror(errno);
478 } else {
479 BtSchedulingTraceCounter = 0;
480 }
481 return logfile_fd;
482 }
483
DebugDump(int fd)484 void DebugDump(int fd) {
485 dprintf(fd, "\nBT Quality Report Events: \n");
486
487 if (kpBqrEventQueue->Empty()) {
488 dprintf(fd, "Event queue is empty.\n");
489 return;
490 }
491
492 while (!kpBqrEventQueue->Empty()) {
493 std::unique_ptr<BqrVseSubEvt> p_event(kpBqrEventQueue->Dequeue());
494
495 bool warning = (p_event->bqr_link_quality_event_.rssi < kCriWarnRssi ||
496 p_event->bqr_link_quality_event_.unused_afh_channel_count >
497 kCriWarnUnusedCh);
498
499 std::stringstream ss_timestamp;
500 ss_timestamp << std::put_time(&p_event->tm_timestamp_, "%m-%d %H:%M:%S");
501
502 dprintf(fd, "%c %s %s\n", warning ? '*' : ' ', ss_timestamp.str().c_str(),
503 p_event->ToString().c_str());
504 }
505
506 dprintf(fd, "\n");
507 }
508
509 } // namespace bqr
510 } // namespace bluetooth
511