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