From 4a73d963a0135302c52db832f1c1bf6c7157f1db Mon Sep 17 00:00:00 2001
From: Ray <kuoray@google.com>
Date: Thu, 3 Jan 2019 21:19:16 +0800
Subject: [PATCH] BQR: Add Bluetooth Quality Report feature

Bluetooth disconnects, voice and audio quality issues are sometimes hard
to debug without an OTA (Over The Air) log.
For debugging this kind of issue we would need to identify it belongs to
the host, Bluetooth controller/firmware, environment or the remote
Bluetooth device.
This feature would route the link and firmware/controller stats (ex: TX
power level, RSSI, Unused AFH channel count...) to the host when quality
events happen.

Quality Monitoring Mode:
The controller will periodically send Bluetooth Quality VSE sub-event to
the host.

Approaching LSTO:
Once no packets are received from the connected Bluetooth device for a
duration longer than the half of LSTO (Link Supervision Timeout) value,
the controller will report Approaching LSTO event to the host.

A2DP Audio Choppy:
When audio stall, the controller will report A2DP Audio Choppy event to
the host.

(e)SCO Voice Choppy:
The controller will report (e)SCO Voice Choppy event to the host if the
voice quality might be bad.

Bug: 111384296
Test: - Verified that all functionalites with the Bluetooth controller /
        firmware which supports Bluetooth Quality Report VSC
	(OpCode: 0xFD5E) and Bluetooth Quality Report (Sub-event code:
	0x58) of VSE.
      - Verified that this feature could work on both the A2DP HW
        offload and non-offload modes.

Change-Id: I3fd3000bace7606855cac3b9b87134499c0ca891
---
 system/btif/Android.bp           |   1 +
 system/btif/include/btif_bqr.h   | 264 ++++++++++++++++++++++++++
 system/btif/src/bluetooth.cc     |   2 +
 system/btif/src/btif_bqr.cc      | 309 +++++++++++++++++++++++++++++++
 system/btif/src/btif_dm.cc       |   4 +-
 system/stack/btm/btm_devctl.cc   |  72 +++++++
 system/stack/btm/btm_int.h       |   2 +
 system/stack/btm/btm_int_types.h |   6 +
 system/stack/include/hcidefs.h   |   6 +
 9 files changed, 665 insertions(+), 1 deletion(-)
 create mode 100644 system/btif/include/btif_bqr.h
 create mode 100644 system/btif/src/btif_bqr.cc

diff --git a/system/btif/Android.bp b/system/btif/Android.bp
index 60a6c69cc6b..7683e16719f 100644
--- a/system/btif/Android.bp
+++ b/system/btif/Android.bp
@@ -53,6 +53,7 @@ cc_library_static {
         "src/btif_avrcp_audio_track.cc",
         "src/btif_ble_advertiser.cc",
         "src/btif_ble_scanner.cc",
+        "src/btif_bqr.cc",
         "src/btif_config.cc",
         "src/btif_config_transcode.cc",
         "src/btif_core.cc",
diff --git a/system/btif/include/btif_bqr.h b/system/btif/include/btif_bqr.h
new file mode 100644
index 00000000000..98c2693b8f4
--- /dev/null
+++ b/system/btif/include/btif_bqr.h
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BTIF_BQR_H_
+#define BTIF_BQR_H_
+
+#include "btm_api_types.h"
+#include "common/leaky_bonded_queue.h"
+
+namespace bluetooth {
+namespace bqr {
+
+// Bluetooth Quality Report (BQR)
+//
+// It is a feature to start the mechanism in the Bluetooth controller to report
+// Bluetooth Quality event to the host and there are four options could be
+// enabled:
+//   [Quality Monitoring Mode]
+//     The controller shall periodically send Bluetooth Quality Report sub-event
+//     to the host.
+//
+//   [Approaching LSTO]
+//     Once no packets are received from the connected Bluetooth device for a
+//     duration longer than the half of LSTO (Link Supervision TimeOut) value,
+//     the controller shall report Approaching LSTO event to the host.
+//
+//   [A2DP Audio Choppy]
+//     When the controller detects the factors which will cause audio choppy,
+//     the controller shall report A2DP Audio Choppy event to the host.
+//
+//   [(e)SCO Voice Choppy]
+//     When the controller detects the factors which will cause voice choppy,
+//     the controller shall report (e)SCO Voice Choppy event to the host.
+
+// Bit masks for the selected quality event reporting.
+static constexpr uint32_t kQualityEventMaskAllOff = 0;
+static constexpr uint32_t kQualityEventMaskMonitorMode = 0x00000001;
+static constexpr uint32_t kQualityEventMaskApproachLsto = 0x00000002;
+static constexpr uint32_t kQualityEventMaskA2dpAudioChoppy = 0x00000004;
+static constexpr uint32_t kQualityEventMaskScoVoiceChoppy = 0x00000008;
+static constexpr uint32_t kQualityEventMaskAll =
+    kQualityEventMaskMonitorMode | kQualityEventMaskApproachLsto |
+    kQualityEventMaskA2dpAudioChoppy | kQualityEventMaskScoVoiceChoppy;
+// Define the minimum time interval (in ms) of quality event reporting for the
+// selected quality event(s). Controller Firmware should not report the next
+// event within the defined time interval.
+static constexpr uint16_t kMinReportIntervalNoLimit = 0;
+static constexpr uint16_t kMinReportIntervalMaxMs = 0xFFFF;
+// Total length of all BQR parameters except Vendor Specific Parameters.
+static constexpr uint8_t kBqrParamTotalLen = 48;
+// Warning criteria of the RSSI value.
+static constexpr int8_t kCriWarnRssi = -80;
+// Warning criteria of the unused AFH channel count.
+static constexpr uint8_t kCriWarnUnusedCh = 55;
+// The queue size of recording the BQR events.
+static constexpr uint8_t kBqrEventQueueSize = 25;
+// The Property of BQR event mask configuration.
+static constexpr const char* kpPropertyEventMask =
+    "persist.bluetooth.bqr.eventmask";
+// The Property of BQR minimum report interval configuration.
+static constexpr const char* kpPropertyReportInt =
+    "persist.bluetooth.bqr.interval";
+
+// Action definition
+//
+// Action to Add, Delete or Clear the reporting of quality event(s).
+// Delete will clear specific quality event(s) reporting. Clear will clear all
+// quality events reporting.
+enum BqrReportAction : uint8_t {
+  REPORT_ACTION_ADD = 0x00,
+  REPORT_ACTION_DELETE = 0x01,
+  REPORT_ACTION_CLEAR = 0x02
+};
+
+// Report ID definition
+enum BqrQualityReportId : uint8_t {
+  QUALITY_REPORT_ID_MONITOR_MODE = 0x01,
+  QUALITY_REPORT_ID_APPROACH_LSTO = 0x02,
+  QUALITY_REPORT_ID_A2DP_AUDIO_CHOPPY = 0x03,
+  QUALITY_REPORT_ID_SCO_VOICE_CHOPPY = 0x04
+};
+
+// Packet Type definition
+enum BqrPacketType : uint8_t {
+  PACKET_TYPE_ID = 0x01,
+  PACKET_TYPE_NULL,
+  PACKET_TYPE_POLL,
+  PACKET_TYPE_FHS,
+  PACKET_TYPE_HV1,
+  PACKET_TYPE_HV2,
+  PACKET_TYPE_HV3,
+  PACKET_TYPE_DV,
+  PACKET_TYPE_EV3,
+  PACKET_TYPE_EV4,
+  PACKET_TYPE_EV5,
+  PACKET_TYPE_2EV3,
+  PACKET_TYPE_2EV5,
+  PACKET_TYPE_3EV3,
+  PACKET_TYPE_3EV5,
+  PACKET_TYPE_DM1,
+  PACKET_TYPE_DH1,
+  PACKET_TYPE_DM3,
+  PACKET_TYPE_DH3,
+  PACKET_TYPE_DM5,
+  PACKET_TYPE_DH5,
+  PACKET_TYPE_AUX1,
+  PACKET_TYPE_2DH1,
+  PACKET_TYPE_2DH3,
+  PACKET_TYPE_2DH5,
+  PACKET_TYPE_3DH1,
+  PACKET_TYPE_3DH3,
+  PACKET_TYPE_3DH5
+};
+
+// Configuration Parameters
+typedef struct {
+  BqrReportAction report_action;
+  uint32_t quality_event_mask;
+  uint16_t minimum_report_interval;
+} BqrConfiguration;
+
+// BQR sub-event of Vendor Specific Event
+class BqrVseSubEvt {
+ public:
+  // Parse the Bluetooth Quality Report VSE sub-event.
+  //
+  // @param length Total length of all parameters contained in the sub-event.
+  // @param p_param_buf A pointer to the parameters contained in the sub-event.
+  // @return false If the parameter total length is abnormal.
+  //         true If all parameters are parsed successfully.
+  bool ParseBqrEvt(uint8_t length, uint8_t* p_param_buf);
+
+  // Get a string representation of the Bluetooth Quality event.
+  //
+  // @return a string representation of the Bluetooth Quality event.
+  std::string ToString() const;
+
+  friend std::ostream& operator<<(std::ostream& os, const BqrVseSubEvt& a) {
+    return os << a.ToString();
+  }
+
+  virtual ~BqrVseSubEvt() = default;
+
+  // Quality report ID.
+  uint8_t quality_report_id_;
+  // Packet type of the connection.
+  uint8_t packet_types_;
+  // Connection handle of the connection.
+  uint16_t connection_handle_;
+  // Performing Role for the connection.
+  uint8_t connection_role_;
+  // Current Transmit Power Level for the connection. This value is the same as
+  // the controller's response to the HCI_Read_Transmit_Power_Level HCI command.
+  uint8_t tx_power_level_;
+  // Received Signal Strength Indication (RSSI) value for the connection. This
+  // value is an absolute receiver signal strength value.
+  int8_t rssi_;
+  // Signal-to-Noise Ratio (SNR) value for the connection. It is the average
+  // SNR of all the channels used by the link currently.
+  uint8_t snr_;
+  // Indicates the number of unused channels in AFH_channel_map.
+  uint8_t unused_afh_channel_count_;
+  // Indicates the number of the channels which are interfered and quality is
+  // bad but are still selected for AFH.
+  uint8_t afh_select_unideal_channel_count_;
+  // Current Link Supervision Timeout Setting.
+  uint16_t lsto_;
+  // Piconet Clock for the specified Connection_Handle. This value is the same
+  // as the controller's response to HCI_Read_Clock HCI command with the
+  // parameter "Which_Clock" of 0x01 (Piconet Clock).
+  uint32_t connection_piconet_clock_;
+  // The count of retransmission.
+  uint32_t retransmission_count_;
+  // The count of no RX.
+  uint32_t no_rx_count_;
+  // The count of NAK (Negative Acknowledge).
+  uint32_t nak_count_;
+  // Timestamp of last TX ACK.
+  uint32_t last_tx_ack_timestamp_;
+  // The count of Flow-off (STOP).
+  uint32_t flow_off_count_;
+  // Timestamp of last Flow-on (GO).
+  uint32_t last_flow_on_timestamp_;
+  // Buffer overflow count (how many bytes of TX data are dropped) since the
+  // last event.
+  uint32_t buffer_overflow_bytes_;
+  // Buffer underflow count (in byte).
+  uint32_t buffer_underflow_bytes_;
+  // Timestamp of receiving BQR VSE sub-event
+  std::tm tm_timestamp_;
+};
+
+// Get a string representation of the Quality Report ID.
+//
+// @param quality_report_id The quality report ID to convert.
+// @return a string representation of the Quality Report ID.
+std::string QualityReportIdToString(uint8_t quality_report_id);
+
+// Get a string representation of the Packet Type.
+//
+// @param packet_type The packet type to convert.
+// @return a string representation of the Packet Type.
+std::string PacketTypeToString(uint8_t packet_type);
+
+// Enable/Disable Bluetooth Quality Report mechanism.
+//
+// Which Quality event will be enabled is according to the setting of the
+// property "persist.bluetooth.bqr.eventmask".
+// And the minimum time interval of quality event reporting depends on the
+// setting of property "persist.bluetooth.bqr.interval".
+//
+// @param is_enable True/False to enable/disable Bluetooth Quality Report
+//   mechanism in the Bluetooth controller.
+void EnableBtQualityReport(bool is_enable);
+
+// Dump Bluetooth Quality Report information.
+//
+// @param fd The file descriptor to use for dumping information.
+void DebugDump(int fd);
+
+// Configure Bluetooth Quality Report setting to the Bluetooth controller.
+//
+// @param bqr_config The struct of configuration parameters.
+void ConfigureBqr(const BqrConfiguration& bqr_config);
+
+// Invoked on completion of Bluetooth Quality Report configuration. Then it will
+// Register/Unregister for receiving VSE - Bluetooth Quality Report sub event.
+//
+// @param current_evt_mask Indicates current quality event bit mask setting in
+//   the Bluetooth controller.
+void ConfigureBqrCmpl(uint32_t current_evt_mask);
+
+// Callback invoked on completion of vendor specific Bluetooth Quality Report
+// command.
+//
+// @param p_vsc_cmpl_params A pointer to the parameters contained in the vendor
+//   specific command complete event.
+void BqrVscCompleteCallback(tBTM_VSC_CMPL* p_vsc_cmpl_params);
+
+// Record a new incoming Bluetooth Quality Report in quality event queue.
+//
+// @param len Lengths of the quality report sent from the Bluetooth
+//   controller.
+// @param p_quality_report A pointer to the quality report which is sent from
+//   the Bluetooth controller via Vendor Specific Event.
+void AddBqrEventToQueue(uint8_t length, uint8_t* p_stream);
+
+}  // namespace bqr
+}  // namespace bluetooth
+
+#endif  // BTIF_BQR_H_
diff --git a/system/btif/src/bluetooth.cc b/system/btif/src/bluetooth.cc
index 5e04e098fbc..1faca2dcb43 100644
--- a/system/btif/src/bluetooth.cc
+++ b/system/btif/src/bluetooth.cc
@@ -53,6 +53,7 @@
 #include "btif_a2dp.h"
 #include "btif_api.h"
 #include "btif_av.h"
+#include "btif_bqr.h"
 #include "btif_config.h"
 #include "btif_debug.h"
 #include "btif_debug_btsnoop.h"
@@ -317,6 +318,7 @@ static void dump(int fd, const char** arguments) {
   alarm_debug_dump(fd);
   HearingAid::DebugDump(fd);
   connection_manager::dump(fd);
+  bluetooth::bqr::DebugDump(fd);
 #if (BTSNOOP_MEM == TRUE)
   btif_debug_btsnoop_dump(fd);
 #endif
diff --git a/system/btif/src/btif_bqr.cc b/system/btif/src/btif_bqr.cc
new file mode 100644
index 00000000000..cf7abeddbf7
--- /dev/null
+++ b/system/btif/src/btif_bqr.cc
@@ -0,0 +1,309 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "btif_bqr.h"
+#include "btif_dm.h"
+#include "common/leaky_bonded_queue.h"
+#include "osi/include/properties.h"
+#include "stack/btm/btm_int.h"
+
+namespace bluetooth {
+namespace bqr {
+
+using bluetooth::common::LeakyBondedQueue;
+using std::chrono::system_clock;
+
+// The instance of BQR event queue
+static std::unique_ptr<LeakyBondedQueue<BqrVseSubEvt>> kpBqrEventQueue(
+    new LeakyBondedQueue<BqrVseSubEvt>(kBqrEventQueueSize));
+
+bool BqrVseSubEvt::ParseBqrEvt(uint8_t length, uint8_t* p_param_buf) {
+  if (length < kBqrParamTotalLen) {
+    LOG(FATAL) << __func__
+               << ": Parameter total length: " << std::to_string(length)
+               << " is abnormal. It shall be not shorter than: "
+               << std::to_string(kBqrParamTotalLen);
+    return false;
+  }
+
+  STREAM_TO_UINT8(quality_report_id_, p_param_buf);
+  STREAM_TO_UINT8(packet_types_, p_param_buf);
+  STREAM_TO_UINT16(connection_handle_, p_param_buf);
+  STREAM_TO_UINT8(connection_role_, p_param_buf);
+  STREAM_TO_UINT8(tx_power_level_, p_param_buf);
+  STREAM_TO_INT8(rssi_, p_param_buf);
+  STREAM_TO_UINT8(snr_, p_param_buf);
+  STREAM_TO_UINT8(unused_afh_channel_count_, p_param_buf);
+  STREAM_TO_UINT8(afh_select_unideal_channel_count_, p_param_buf);
+  STREAM_TO_UINT16(lsto_, p_param_buf);
+  STREAM_TO_UINT32(connection_piconet_clock_, p_param_buf);
+  STREAM_TO_UINT32(retransmission_count_, p_param_buf);
+  STREAM_TO_UINT32(no_rx_count_, p_param_buf);
+  STREAM_TO_UINT32(nak_count_, p_param_buf);
+  STREAM_TO_UINT32(last_tx_ack_timestamp_, p_param_buf);
+  STREAM_TO_UINT32(flow_off_count_, p_param_buf);
+  STREAM_TO_UINT32(last_flow_on_timestamp_, p_param_buf);
+  STREAM_TO_UINT32(buffer_overflow_bytes_, p_param_buf);
+  STREAM_TO_UINT32(buffer_underflow_bytes_, p_param_buf);
+
+  const auto now = system_clock::to_time_t(system_clock::now());
+  localtime_r(&now, &tm_timestamp_);
+
+  return true;
+}
+
+std::string BqrVseSubEvt::ToString() const {
+  std::stringstream ss_return_string;
+  ss_return_string << QualityReportIdToString(quality_report_id_)
+                   << ", Handle: " << loghex(connection_handle_) << ", "
+                   << PacketTypeToString(packet_types_) << ", "
+                   << ((connection_role_ == 0) ? "Master" : "Slave ")
+                   << ", PwLv: " << loghex(tx_power_level_)
+                   << ", RSSI: " << std::to_string(rssi_)
+                   << ", SNR: " << std::to_string(snr_) << ", UnusedCh: "
+                   << std::to_string(unused_afh_channel_count_)
+                   << ", UnidealCh: "
+                   << std::to_string(afh_select_unideal_channel_count_)
+                   << ", ReTx: " << std::to_string(retransmission_count_)
+                   << ", NoRX: " << std::to_string(no_rx_count_)
+                   << ", NAK: " << std::to_string(nak_count_)
+                   << ", FlowOff: " << std::to_string(flow_off_count_)
+                   << ", OverFlow: " << std::to_string(buffer_overflow_bytes_)
+                   << ", UndFlow: " << std::to_string(buffer_underflow_bytes_);
+  return ss_return_string.str();
+}
+
+std::string QualityReportIdToString(uint8_t quality_report_id) {
+  switch (quality_report_id) {
+    case QUALITY_REPORT_ID_MONITOR_MODE:
+      return "Monitoring ";
+    case QUALITY_REPORT_ID_APPROACH_LSTO:
+      return "Appro LSTO ";
+    case QUALITY_REPORT_ID_A2DP_AUDIO_CHOPPY:
+      return "A2DP Choppy";
+    case QUALITY_REPORT_ID_SCO_VOICE_CHOPPY:
+      return "SCO Choppy ";
+    default:
+      return "Invalid    ";
+  }
+}
+
+std::string PacketTypeToString(uint8_t packet_type) {
+  switch (packet_type) {
+    case PACKET_TYPE_ID:
+      return "ID";
+    case PACKET_TYPE_NULL:
+      return "NULL";
+    case PACKET_TYPE_POLL:
+      return "POLL";
+    case PACKET_TYPE_FHS:
+      return "FHS";
+    case PACKET_TYPE_HV1:
+      return "HV1";
+    case PACKET_TYPE_HV2:
+      return "HV2";
+    case PACKET_TYPE_HV3:
+      return "HV3";
+    case PACKET_TYPE_DV:
+      return "DV";
+    case PACKET_TYPE_EV3:
+      return "EV3";
+    case PACKET_TYPE_EV4:
+      return "EV4";
+    case PACKET_TYPE_EV5:
+      return "EV5";
+    case PACKET_TYPE_2EV3:
+      return "2EV3";
+    case PACKET_TYPE_2EV5:
+      return "2EV5";
+    case PACKET_TYPE_3EV3:
+      return "3EV3";
+    case PACKET_TYPE_3EV5:
+      return "3EV5";
+    case PACKET_TYPE_DM1:
+      return "DM1";
+    case PACKET_TYPE_DH1:
+      return "DH1";
+    case PACKET_TYPE_DM3:
+      return "DM3";
+    case PACKET_TYPE_DH3:
+      return "DH3";
+    case PACKET_TYPE_DM5:
+      return "DM5";
+    case PACKET_TYPE_DH5:
+      return "DH5";
+    case PACKET_TYPE_AUX1:
+      return "AUX1";
+    case PACKET_TYPE_2DH1:
+      return "2DH1";
+    case PACKET_TYPE_2DH3:
+      return "2DH3";
+    case PACKET_TYPE_2DH5:
+      return "2DH5";
+    case PACKET_TYPE_3DH1:
+      return "3DH1";
+    case PACKET_TYPE_3DH3:
+      return "3DH3";
+    case PACKET_TYPE_3DH5:
+      return "3DH5";
+    default:
+      return "UnKnown ";
+  }
+}
+
+void AddBqrEventToQueue(uint8_t length, uint8_t* p_stream) {
+  std::unique_ptr<BqrVseSubEvt> p_bqr_event = std::make_unique<BqrVseSubEvt>();
+  if (!p_bqr_event->ParseBqrEvt(length, p_stream)) {
+    LOG(WARNING) << __func__ << ": Fail to parse BQR sub event.";
+    return;
+  }
+
+  LOG(WARNING) << *p_bqr_event;
+  kpBqrEventQueue->Enqueue(p_bqr_event.release());
+}
+
+void ConfigureBqrCmpl(uint32_t current_evt_mask) {
+  LOG(INFO) << __func__ << ": current_evt_mask: " << loghex(current_evt_mask);
+  // (Un)Register for VSE of Bluetooth Quality Report sub event
+  tBTM_STATUS btm_status = BTM_BT_Quality_Report_VSE_Register(
+      current_evt_mask > kQualityEventMaskAllOff, AddBqrEventToQueue);
+
+  if (btm_status != BTM_SUCCESS) {
+    LOG(ERROR) << __func__ << ": Fail to (un)register VSE of BQR sub event."
+               << " status: " << btm_status;
+  }
+}
+
+void EnableBtQualityReport(bool is_enable) {
+  LOG(INFO) << __func__ << ": is_enable: " << logbool(is_enable);
+
+  char bqr_prop_evtmask[PROPERTY_VALUE_MAX] = {0};
+  char bqr_prop_interval[PROPERTY_VALUE_MAX] = {0};
+  osi_property_get(kpPropertyEventMask, bqr_prop_evtmask, "");
+  osi_property_get(kpPropertyReportInt, bqr_prop_interval, "");
+
+  if (strlen(bqr_prop_evtmask) == 0 || strlen(bqr_prop_interval) == 0) {
+    LOG(WARNING) << __func__ << ": Bluetooth Quality Report is disabled."
+                 << " bqr_prop_evtmask: " << bqr_prop_evtmask
+                 << ", bqr_prop_interval: " << bqr_prop_interval;
+    return;
+  }
+
+  BqrConfiguration bqr_config = {};
+
+  if (is_enable) {
+    bqr_config.report_action = REPORT_ACTION_ADD;
+    bqr_config.quality_event_mask =
+        static_cast<uint32_t>(atoi(bqr_prop_evtmask));
+    bqr_config.minimum_report_interval =
+        static_cast<uint16_t>(atoi(bqr_prop_interval));
+  } else {
+    bqr_config.report_action = REPORT_ACTION_CLEAR;
+    bqr_config.quality_event_mask = kQualityEventMaskAllOff;
+    bqr_config.minimum_report_interval = kMinReportIntervalNoLimit;
+  }
+
+  LOG(INFO) << __func__
+            << ": Event Mask: " << loghex(bqr_config.quality_event_mask)
+            << ", Interval: " << bqr_config.minimum_report_interval;
+  ConfigureBqr(bqr_config);
+}
+
+void BqrVscCompleteCallback(tBTM_VSC_CMPL* p_vsc_cmpl_params) {
+  if (p_vsc_cmpl_params->param_len < 1) {
+    LOG(FATAL) << __func__
+               << ": The length of returned parameters is less than 1";
+    return;
+  }
+
+  uint8_t* p_event_param_buf = p_vsc_cmpl_params->p_param_buf;
+  uint8_t status = 0xff;
+  // [Return Parameter]         | [Size]   | [Purpose]
+  // Status                     | 1 octet  | Command complete status
+  // Current_Quality_Event_Mask | 4 octets | Indicates current bit mask setting
+  STREAM_TO_UINT8(status, p_event_param_buf);
+  if (status != HCI_SUCCESS) {
+    LOG(FATAL) << __func__
+               << ": Fail to configure BQR. status: " << loghex(status);
+    return;
+  }
+
+  if (p_vsc_cmpl_params->param_len != 5) {
+    LOG(FATAL) << __func__
+               << ": The length of returned parameters is not equal to 5: "
+               << std::to_string(p_vsc_cmpl_params->param_len);
+    return;
+  }
+
+  uint32_t current_quality_event_mask = kQualityEventMaskAllOff;
+  STREAM_TO_UINT32(current_quality_event_mask, p_event_param_buf);
+
+  LOG(INFO) << __func__
+            << ", current event mask: " << loghex(current_quality_event_mask);
+  ConfigureBqrCmpl(current_quality_event_mask);
+}
+
+void ConfigureBqr(const BqrConfiguration& bqr_config) {
+  if (bqr_config.report_action > REPORT_ACTION_CLEAR ||
+      bqr_config.quality_event_mask > kQualityEventMaskAll ||
+      bqr_config.minimum_report_interval > kMinReportIntervalMaxMs) {
+    LOG(FATAL) << __func__ << ": Invalid Parameter"
+               << ", Action: " << bqr_config.report_action
+               << ", Mask: " << loghex(bqr_config.quality_event_mask)
+               << ", Interval: " << bqr_config.minimum_report_interval;
+    return;
+  }
+
+  LOG(INFO) << __func__ << ": Action: " << bqr_config.report_action
+            << ", Mask: " << loghex(bqr_config.quality_event_mask)
+            << ", Interval: " << bqr_config.minimum_report_interval;
+
+  uint8_t param[sizeof(BqrConfiguration)];
+  uint8_t* p_param = param;
+  UINT8_TO_STREAM(p_param, bqr_config.report_action);
+  UINT32_TO_STREAM(p_param, bqr_config.quality_event_mask);
+  UINT16_TO_STREAM(p_param, bqr_config.minimum_report_interval);
+
+  BTM_VendorSpecificCommand(HCI_CONTROLLER_BQR_OPCODE_OCF, p_param - param,
+                            param, BqrVscCompleteCallback);
+}
+
+void DebugDump(int fd) {
+  dprintf(fd, "\nBT Quality Report Events: \n");
+
+  if (kpBqrEventQueue->Empty()) {
+    dprintf(fd, "Event queue is empty.\n");
+    return;
+  }
+
+  while (!kpBqrEventQueue->Empty()) {
+    std::unique_ptr<BqrVseSubEvt> p_event(kpBqrEventQueue->Dequeue());
+
+    bool warning = (p_event->rssi_ < kCriWarnRssi ||
+                    p_event->unused_afh_channel_count_ > kCriWarnUnusedCh);
+
+    std::stringstream ss_timestamp;
+    ss_timestamp << std::put_time(&p_event->tm_timestamp_, "%m-%d %H:%M:%S");
+
+    dprintf(fd, "%c  %s %s\n", warning ? '*' : ' ', ss_timestamp.str().c_str(),
+            p_event->ToString().c_str());
+  }
+
+  dprintf(fd, "\n");
+}
+
+}  // namespace bqr
+}  // namespace bluetooth
diff --git a/system/btif/src/btif_dm.cc b/system/btif/src/btif_dm.cc
index dc6adcfc7c6..683834ab527 100644
--- a/system/btif/src/btif_dm.cc
+++ b/system/btif/src/btif_dm.cc
@@ -50,6 +50,7 @@
 #include "bta_gatt_api.h"
 #include "btif_api.h"
 #include "btif_av.h"
+#include "btif_bqr.h"
 #include "btif_config.h"
 #include "btif_dm.h"
 #include "btif_hd.h"
@@ -1606,7 +1607,7 @@ static void btif_dm_upstreams_evt(uint16_t event, char* p_param) {
       ** and bonded_devices_info_cb
       */
       btif_storage_load_bonded_devices();
-
+      bluetooth::bqr::EnableBtQualityReport(true);
       btif_enable_bluetooth_evt(p_data->enable.status);
     } break;
 
@@ -1620,6 +1621,7 @@ static void btif_dm_upstreams_evt(uint16_t event, char* p_param) {
           btif_in_execute_service_request(i, false);
         }
       }
+      bluetooth::bqr::EnableBtQualityReport(false);
       btif_disable_bluetooth_evt();
       break;
 
diff --git a/system/stack/btm/btm_devctl.cc b/system/stack/btm/btm_devctl.cc
index 04721f83d79..bc3fb5649ac 100644
--- a/system/stack/btm/btm_devctl.cc
+++ b/system/stack/btm/btm_devctl.cc
@@ -67,6 +67,7 @@ extern bluetooth::common::MessageLoopThread bt_startup_thread;
 
 static void btm_decode_ext_features_page(uint8_t page_number,
                                          const BD_FEATURES p_features);
+static void BTM_BT_Quality_Report_VSE_CBack(uint8_t length, uint8_t* p_stream);
 
 /*******************************************************************************
  *
@@ -865,3 +866,74 @@ void btm_report_device_status(tBTM_DEV_STATUS status) {
   /* Call the call back to pass the device status to application */
   if (p_cb) (*p_cb)(status);
 }
+
+/*******************************************************************************
+ *
+ * Function         BTM_BT_Quality_Report_VSE_CBack
+ *
+ * Description      Callback invoked on receiving of Vendor Specific Events.
+ *                  This function will call registered BQR report receiver if
+ *                  Bluetooth Quality Report sub-event is identified.
+ *
+ * Parameters:      length - Lengths of all of the parameters contained in the
+ *                    Vendor Specific Event.
+ *                  p_stream - A pointer to the quality report which is sent
+ *                    from the Bluetooth controller via Vendor Specific Event.
+ *
+ ******************************************************************************/
+static void BTM_BT_Quality_Report_VSE_CBack(uint8_t length, uint8_t* p_stream) {
+  if (length == 0) {
+    LOG(WARNING) << __func__ << ": Lengths of all of the parameters are zero.";
+    return;
+  }
+
+  uint8_t sub_event = 0;
+  STREAM_TO_UINT8(sub_event, p_stream);
+  length--;
+
+  if (sub_event == HCI_VSE_SUBCODE_BQR_SUB_EVT) {
+    LOG(INFO) << __func__
+              << ": BQR sub event, report length: " << std::to_string(length);
+
+    if (btm_cb.p_bqr_report_receiver == nullptr) {
+      LOG(WARNING) << __func__ << ": No registered report receiver.";
+      return;
+    }
+
+    btm_cb.p_bqr_report_receiver(length, p_stream);
+  }
+}
+
+/*******************************************************************************
+ *
+ * Function         BTM_BT_Quality_Report_VSE_Register
+ *
+ * Description      Register/Deregister for Bluetooth Quality Report VSE sub
+ *                  event Callback.
+ *
+ * Parameters:      is_register - True/False to register/unregister for VSE.
+ *                  p_bqr_report_receiver - The receiver for receiving Bluetooth
+ *                    Quality Report VSE sub event.
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_BT_Quality_Report_VSE_Register(
+    bool is_register, tBTM_BT_QUALITY_REPORT_RECEIVER* p_bqr_report_receiver) {
+  tBTM_STATUS retval =
+      BTM_RegisterForVSEvents(BTM_BT_Quality_Report_VSE_CBack, is_register);
+
+  if (retval != BTM_SUCCESS) {
+    LOG(WARNING) << __func__ << ": Fail to (un)register VSEvents: " << retval
+                 << ", is_register: " << logbool(is_register);
+    return retval;
+  }
+
+  if (is_register) {
+    btm_cb.p_bqr_report_receiver = p_bqr_report_receiver;
+  } else {
+    btm_cb.p_bqr_report_receiver = nullptr;
+  }
+
+  LOG(INFO) << __func__ << ": Success to (un)register VSEvents."
+            << " is_register: " << logbool(is_register);
+  return retval;
+}
diff --git a/system/stack/btm/btm_int.h b/system/stack/btm/btm_int.h
index d06322270c1..8f2274b1f4d 100644
--- a/system/stack/btm/btm_int.h
+++ b/system/stack/btm/btm_int.h
@@ -191,6 +191,8 @@ extern void btm_inq_db_reset(void);
 extern void btm_vendor_specific_evt(uint8_t* p, uint8_t evt_len);
 extern void btm_delete_stored_link_key_complete(uint8_t* p);
 extern void btm_report_device_status(tBTM_DEV_STATUS status);
+extern tBTM_STATUS BTM_BT_Quality_Report_VSE_Register(
+    bool is_register, tBTM_BT_QUALITY_REPORT_RECEIVER* p_bqr_report_receiver);
 
 /* Internal functions provided by btm_dev.cc
  *********************************************
diff --git a/system/stack/btm/btm_int_types.h b/system/stack/btm/btm_int_types.h
index 7e51ff93820..a0460b1c0eb 100644
--- a/system/stack/btm/btm_int_types.h
+++ b/system/stack/btm/btm_int_types.h
@@ -18,6 +18,7 @@
 #ifndef BTM_INT_TYPES_H
 #define BTM_INT_TYPES_H
 
+#include "btif/include/btif_bqr.h"
 #include "btm_api_types.h"
 #include "btm_ble_api_types.h"
 #include "btm_ble_int_types.h"
@@ -724,6 +725,9 @@ typedef struct {
 #define CONN_ORIENT_ORIG true
 typedef bool CONNECTION_TYPE;
 
+// Bluetooth Quality Report - Report receiver
+typedef void(tBTM_BT_QUALITY_REPORT_RECEIVER)(uint8_t len, uint8_t* p_stream);
+
 /* Define a structure to hold all the BTM data
 */
 
@@ -828,6 +832,8 @@ typedef struct {
                                    tBTM_SEC_QUEUE_ENTRY format */
 
   char state_temp_buffer[BTM_STATE_BUFFER_SIZE];
+  // BQR Receiver
+  tBTM_BT_QUALITY_REPORT_RECEIVER* p_bqr_report_receiver;
 } tBTM_CB;
 
 /* security action for L2CAP COC channels */
diff --git a/system/stack/include/hcidefs.h b/system/stack/include/hcidefs.h
index db58163b60c..ef87b5b615d 100644
--- a/system/stack/include/hcidefs.h
+++ b/system/stack/include/hcidefs.h
@@ -423,6 +423,9 @@
 /* A2DP offload OCF */
 #define HCI_CONTROLLER_A2DP_OPCODE_OCF (0x015D | HCI_GRP_VENDOR_SPECIFIC)
 
+/* Bluetooth Quality Report OCF */
+#define HCI_CONTROLLER_BQR_OPCODE_OCF (0x015E | HCI_GRP_VENDOR_SPECIFIC)
+
 /* subcode for multi adv feature */
 #define BTM_BLE_MULTI_ADV_SET_PARAM 0x01
 #define BTM_BLE_MULTI_ADV_WRITE_ADV_DATA 0x02
@@ -449,6 +452,9 @@
 /* debug info sub event */
 #define HCI_VSE_SUBCODE_DEBUG_INFO_SUB_EVT 0x57
 
+/* Bluetooth Quality Report sub event */
+#define HCI_VSE_SUBCODE_BQR_SUB_EVT 0x58
+
 /* LE supported states definition */
 #define HCI_LE_ADV_STATE 0x00000001
 #define HCI_LE_SCAN_STATE 0x00000002
-- 
GitLab