diff --git a/system/btif/Android.bp b/system/btif/Android.bp
index 60a6c69cc6b05920f527da724c706e1af6213acc..7683e16719fe89c738af8efdddca64e3ec9e2832 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 0000000000000000000000000000000000000000..98c2693b8f48d4457a9c80c003041252b9462ea8
--- /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 5e04e098fbcac7c7b15b5deb19f30340924d54e0..1faca2dcb4349cee515539a9b0ebc0ed5f160f80 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 0000000000000000000000000000000000000000..cf7abeddbf79ff35b674dc29493cc565f77be637
--- /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 dc6adcfc7c617a815b186d34e1cbf65f02f9aa5b..683834ab52757250bbcebe1ea677782625401396 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 04721f83d7931bd7667613ce9ddb78c977a5c096..bc3fb5649acede756b5c847f967dc5e1e3607805 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 d06322270c1778d557328a5565d55c35288c8f13..8f2274b1f4ddf7f19c66069ce9e93e839c8c6865 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 7e51ff93820125e3c10ac2f279ee91550caaa4b1..a0460b1c0eb9f1348f9b6da612a5e7270478d5d1 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 db58163b60ce20132aaec72f4f115fc1834d645e..ef87b5b615d3d5f0c68f6199c43d47aad733de29 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