Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 17539d5f authored by Ray's avatar Ray Committed by android-build-merger
Browse files

BQR: Add Bluetooth Quality Report feature

am: 4a73d963

Change-Id: I7f3684b437ea914eb446bcdafb83f19d870944de
parents a318b1bc 4a73d963
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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",
+264 −0
Original line number Diff line number Diff line
/*
 * 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_
+2 −0
Original line number Diff line number Diff line
@@ -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
+309 −0
Original line number Diff line number Diff line
/*
 * 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
+3 −1
Original line number Diff line number Diff line
@@ -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;

Loading