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

Commit 1dc3e33e authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "vc: Improve initial volume control operations" into main

parents 5bf1a095 7afee0ee
Loading
Loading
Loading
Loading
+0 −3
Original line number Diff line number Diff line
@@ -956,9 +956,6 @@ public class VolumeControlService extends ProfileService {
         * Offset ids a countinous from 1 to number_of_ext_outputs*/
        for (int i = 1; i <= numberOfExternalOutputs; i++) {
            offsets.add(i);
            mVolumeControlNativeInterface.getExtAudioOutVolumeOffset(device, i);
            mVolumeControlNativeInterface.getExtAudioOutLocation(device, i);
            mVolumeControlNativeInterface.getExtAudioOutDescription(device, i);
        }
    }

+2 −0
Original line number Diff line number Diff line
@@ -676,6 +676,7 @@ cc_test {
        "liblog",
    ],
    static_libs: [
        "bluetooth_flags_c_lib_for_test",
        "libbluetooth-types",
        "libbluetooth_crypto_toolbox",
        "libbluetooth_gd",
@@ -686,6 +687,7 @@ cc_test {
        "libbt_shim_ffi",
        "libchrome",
        "libgmock",
        "server_configurable_flags",
    ],
    sanitize: {
        cfi: false,
+63 −17
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
 */

#include <bluetooth/log.h>
#include <com_android_bluetooth_flags.h>

#include <map>
#include <vector>
@@ -262,26 +263,50 @@ bool VolumeControlDevice::subscribe_for_notifications(tGATT_IF gatt_if, uint16_t
 */
bool VolumeControlDevice::EnqueueInitialRequests(tGATT_IF gatt_if, GATT_READ_OP_CB chrc_read_cb,
                                                 GATT_WRITE_OP_CB cccd_write_cb) {
  log::debug("{}", address);

  std::map<uint16_t, uint16_t> hdls_to_subscribe{
          {volume_state_handle, volume_state_ccc_handle},
  };

  handles_pending.clear();
  // Status and Flags are mandatory
  handles_pending.insert(volume_state_handle);
  handles_pending.insert(volume_state_ccc_handle);
  if (!subscribe_for_notifications(gatt_if, volume_state_handle, volume_state_ccc_handle,
                                   cccd_write_cb)) {
    return false;

  handles_pending.insert(volume_flags_handle);

  if (GATT_HANDLE_IS_VALID(volume_flags_ccc_handle)) {
    hdls_to_subscribe[volume_flags_handle] = volume_flags_ccc_handle;
    handles_pending.insert(volume_flags_ccc_handle);
  }

  for (auto const& offset : audio_offsets.volume_offsets) {
    handles_pending.insert(offset.state_handle);
    hdls_to_subscribe[offset.state_handle] = offset.state_ccc_handle;
    handles_pending.insert(offset.state_ccc_handle);
    if (!subscribe_for_notifications(gatt_if, offset.state_handle, offset.state_ccc_handle,
                                     cccd_write_cb)) {
      return false;

    if (GATT_HANDLE_IS_VALID(offset.audio_descr_ccc_handle)) {
      hdls_to_subscribe[offset.audio_descr_handle] = offset.audio_descr_ccc_handle;
      handles_pending.insert(offset.audio_descr_ccc_handle);
    }

    BtaGattQueue::ReadCharacteristic(connection_id, offset.state_handle, chrc_read_cb, nullptr);
    if (GATT_HANDLE_IS_VALID(offset.audio_location_ccc_handle)) {
      hdls_to_subscribe[offset.audio_location_handle] = offset.audio_location_ccc_handle;
      handles_pending.insert(offset.audio_location_ccc_handle);
    }
  }

  for (auto const& handles : hdls_to_subscribe) {
    log::debug("{}, handle={:#x}, ccc_handle={:#x}", address, handles.first, handles.second);
    if (!subscribe_for_notifications(gatt_if, handles.first, handles.second, cccd_write_cb)) {
      log::error("{}, failed to subscribe for handle={:#x}, ccc_handle={:#x}", address,
                 handles.first, handles.second);
      return false;
    }
  }

  BtaGattQueue::ReadCharacteristic(connection_id, volume_state_handle, chrc_read_cb, nullptr);
  BtaGattQueue::ReadCharacteristic(connection_id, volume_flags_handle, chrc_read_cb, nullptr);

  return true;
}
@@ -293,21 +318,42 @@ bool VolumeControlDevice::EnqueueInitialRequests(tGATT_IF gatt_if, GATT_READ_OP_
 * In each case we subscribe first to be sure we do not miss any value change.
 */
void VolumeControlDevice::EnqueueRemainingRequests(tGATT_IF gatt_if, GATT_READ_OP_CB chrc_read_cb,
                                                   GATT_READ_MULTI_OP_CB chrc_multi_read_cb,
                                                   GATT_WRITE_OP_CB cccd_write_cb) {
  std::map<uint16_t, uint16_t> handle_pairs{
          {volume_flags_handle, volume_flags_ccc_handle},
  };
  std::vector<uint16_t> handles_to_read;

  for (auto const& offset : audio_offsets.volume_offsets) {
    handle_pairs[offset.audio_location_handle] = offset.audio_location_ccc_handle;
    handle_pairs[offset.audio_descr_handle] = offset.audio_descr_ccc_handle;
    handles_to_read.push_back(offset.state_handle);
    handles_to_read.push_back(offset.audio_location_handle);
    handles_to_read.push_back(offset.audio_descr_handle);
  }

  for (auto const& handles : handle_pairs) {
    if (GATT_HANDLE_IS_VALID(handles.second)) {
      subscribe_for_notifications(gatt_if, handles.first, handles.second, cccd_write_cb);
  log::debug("{}, number of handles={}", address, handles_to_read.size());

  if (!com::android::bluetooth::flags::le_ase_read_multiple_variable()) {
    for (auto const& handle : handles_to_read) {
      BtaGattQueue::ReadCharacteristic(connection_id, handle, chrc_read_cb, nullptr);
    }
    BtaGattQueue::ReadCharacteristic(connection_id, handles.first, chrc_read_cb, nullptr);
    return;
  }

  size_t sent_cnt = 0;

  while (sent_cnt < handles_to_read.size()) {
    tBTA_GATTC_MULTI multi_read{};
    size_t remain_cnt = (handles_to_read.size() - sent_cnt);

    multi_read.num_attr =
            remain_cnt > GATT_MAX_READ_MULTI_HANDLES ? GATT_MAX_READ_MULTI_HANDLES : remain_cnt;

    auto handles_begin = handles_to_read.begin() + sent_cnt;
    std::copy(handles_begin, handles_begin + multi_read.num_attr, multi_read.handles);

    sent_cnt += multi_read.num_attr;
    log::debug{"{}, calling multi with {} attributes, sent_cnt {} ", address, multi_read.num_attr,
               sent_cnt};

    BtaGattQueue::ReadMultiCharacteristic(connection_id, multi_read, chrc_multi_read_cb, nullptr);
  }
}

+1 −0
Original line number Diff line number Diff line
@@ -136,6 +136,7 @@ public:
  bool EnqueueInitialRequests(tGATT_IF gatt_if, GATT_READ_OP_CB chrc_read_cb,
                              GATT_WRITE_OP_CB cccd_write_cb);
  void EnqueueRemainingRequests(tGATT_IF gatt_if, GATT_READ_OP_CB chrc_read_cb,
                                GATT_READ_MULTI_OP_CB chrc_multi_read,
                                GATT_WRITE_OP_CB cccd_write_cb);
  bool VerifyReady(uint16_t handle);
  bool IsReady() { return device_ready; }
+85 −24
Original line number Diff line number Diff line
@@ -17,8 +17,10 @@

#include "bta/vc/devices.h"

#include <com_android_bluetooth_flags.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <log/log.h>

#include <list>
#include <map>
@@ -40,6 +42,7 @@ using ::testing::DoAll;
using ::testing::Invoke;
using ::testing::Mock;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::SetArgPointee;
using ::testing::Test;

@@ -52,12 +55,15 @@ RawAddress GetTestAddress(int index) {
class VolumeControlDevicesTest : public ::testing::Test {
protected:
  void SetUp() override {
    __android_log_set_minimum_priority(ANDROID_LOG_VERBOSE);
    devices_ = new VolumeControlDevices();
    gatt::SetMockBtaGattInterface(&gatt_interface);
    gatt::SetMockBtaGattQueue(&gatt_queue);
  }

  void TearDown() override {
    com::android::bluetooth::flags::provider_->reset_flags();

    gatt::SetMockBtaGattQueue(nullptr);
    gatt::SetMockBtaGattInterface(nullptr);
    delete devices_;
@@ -211,6 +217,7 @@ TEST_F(VolumeControlDevicesTest, test_control_point_skip_not_connected) {
class VolumeControlDeviceTest : public ::testing::Test {
protected:
  void SetUp() override {
    __android_log_set_minimum_priority(ANDROID_LOG_VERBOSE);
    device = new VolumeControlDevice(GetTestAddress(1), true);
    gatt::SetMockBtaGattInterface(&gatt_interface);
    gatt::SetMockBtaGattQueue(&gatt_queue);
@@ -242,9 +249,13 @@ protected:
            }));

    ON_CALL(gatt_interface, GetServices(_)).WillByDefault(Return(&services));

    ON_CALL(gatt_interface, RegisterForNotifications(_, _, _))
            .WillByDefault(DoAll(Return(GATT_SUCCESS)));
  }

  void TearDown() override {
    com::android::bluetooth::flags::provider_->reset_flags();
    bluetooth::manager::SetMockBtmInterface(nullptr);
    gatt::SetMockBtaGattQueue(nullptr);
    gatt::SetMockBtaGattInterface(nullptr);
@@ -254,14 +265,18 @@ protected:
  /* sample database 1xVCS, 2xAICS, 2xVOCS */
  void SetSampleDatabase1(void) {
    gatt::DatabaseBuilder builder;
    builder.AddService(0x0001, 0x0016, kVolumeControlUuid, true);
    builder.AddService(0x0001, 0x0017, kVolumeControlUuid, true);
    builder.AddIncludedService(0x0004, kVolumeOffsetUuid, 0x0060, 0x0069);
    builder.AddIncludedService(0x0005, kVolumeOffsetUuid, 0x0080, 0x008b);
    builder.AddCharacteristic(0x0010, 0x0011, kVolumeControlStateUuid,
                              GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY);
    builder.AddDescriptor(0x0012, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG));
    builder.AddCharacteristic(0x0013, 0x0014, kVolumeControlPointUuid, GATT_CHAR_PROP_BIT_WRITE);
    builder.AddCharacteristic(0x0015, 0x0016, kVolumeFlagsUuid, GATT_CHAR_PROP_BIT_READ);
    builder.AddCharacteristic(0x0015, 0x0016, kVolumeFlagsUuid,
                              GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY);
    builder.AddDescriptor(0x0017, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG));

    // First VOCS
    builder.AddService(0x0060, 0x0069, kVolumeOffsetUuid, false);
    builder.AddCharacteristic(0x0061, 0x0062, kVolumeOffsetStateUuid,
                              GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY);
@@ -271,6 +286,8 @@ protected:
                              GATT_CHAR_PROP_BIT_WRITE);
    builder.AddCharacteristic(0x0068, 0x0069, kVolumeOffsetOutputDescriptionUuid,
                              GATT_CHAR_PROP_BIT_READ);

    // Second VOCS
    builder.AddService(0x0080, 0x008b, kVolumeOffsetUuid, false);
    builder.AddCharacteristic(0x0081, 0x0082, kVolumeOffsetStateUuid,
                              GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY);
@@ -432,12 +449,18 @@ TEST_F(VolumeControlDeviceTest, test_enqueue_initial_requests) {
  tGATT_IF gatt_if = 0x0001;
  std::vector<uint8_t> register_for_notification_data({0x01, 0x00});

  std::map<uint16_t, uint16_t> expected_to_read_write{{0x0011, 0x0012} /* volume control state */,
  std::map<uint16_t, uint16_t> expected_subscribtions{
          {0x0011, 0x0012} /* volume control state */,
          {0x0016, 0x0017} /* volume control flags */,
          {0x0062, 0x0063} /* volume offset state 1 */,
                                                      {0x0082, 0x0083} /* volume offset state 2 */};

  for (auto const& handle_pair : expected_to_read_write) {
    EXPECT_CALL(gatt_queue, ReadCharacteristic(_, handle_pair.first, _, _));
          {0x0082, 0x0083} /* volume offset state 2 */,
          {0x0085, 0x0086} /* volume offset location 2 */,
          {0x008a, 0x008b} /* volume offset description 2 */};
  // Expected read for state and flags  Volume State
  EXPECT_CALL(gatt_queue, ReadCharacteristic(_, 0x0011, _, _));
  EXPECT_CALL(gatt_queue, ReadCharacteristic(_, 0x0016, _, _));

  for (auto const& handle_pair : expected_subscribtions) {
    EXPECT_CALL(gatt_queue, WriteDescriptor(_, handle_pair.second, register_for_notification_data,
                                            GATT_WRITE, _, _));
    EXPECT_CALL(gatt_interface, RegisterForNotifications(gatt_if, _, handle_pair.first));
@@ -448,6 +471,8 @@ TEST_F(VolumeControlDeviceTest, test_enqueue_initial_requests) {
  auto cccd_write_cb = [](uint16_t conn_id, tGATT_STATUS status, uint16_t handle, uint16_t len,
                          const uint8_t* value, void* data) {};
  ASSERT_EQ(true, device->EnqueueInitialRequests(gatt_if, chrc_read_cb, cccd_write_cb));
  Mock::VerifyAndClearExpectations(&gatt_queue);
  Mock::VerifyAndClearExpectations(&gatt_interface);
}

TEST_F(VolumeControlDeviceTest, test_device_ready) {
@@ -487,36 +512,72 @@ TEST_F(VolumeControlDeviceTest, test_device_ready) {
}

TEST_F(VolumeControlDeviceTest, test_enqueue_remaining_requests) {
  com::android::bluetooth::flags::provider_->le_ase_read_multiple_variable(false);

  SetSampleDatabase1();

  tGATT_IF gatt_if = 0x0001;
  std::vector<uint8_t> register_for_notification_data({0x01, 0x00});

  std::vector<uint16_t> expected_to_read{0x0016 /* volume flags */, 0x0065 /* audio location 1 */,
                                         0x0069 /* audio output description 1 */,
                                         0x0085 /* audio location 1 */,
                                         0x008a /* audio output description 1 */};

  std::map<uint16_t, uint16_t> expected_to_write_value_ccc_handle_map{
          {0x0085, 0x0086} /* audio location ccc 2 */,
          {0x008a, 0x008b} /* audio output description ccc */
  };
  std::vector<uint16_t> expected_to_read{
          0x0062 /* audio output state 1 */,       0x0065 /* audio output location 1 */,
          0x0069 /* audio output description 1 */, 0x0082 /* audio output state 1 */,
          0x0085 /* audio output location 1 */,    0x008a /* audio output description 1 */};

  for (uint16_t handle : expected_to_read) {
    EXPECT_CALL(gatt_queue, ReadCharacteristic(_, handle, _, _));
  }

  for (auto const& handle_pair : expected_to_write_value_ccc_handle_map) {
    EXPECT_CALL(gatt_queue, WriteDescriptor(_, handle_pair.second, register_for_notification_data,
                                            GATT_WRITE, _, _));
    EXPECT_CALL(gatt_interface, RegisterForNotifications(gatt_if, _, handle_pair.first));
  EXPECT_CALL(gatt_queue, WriteDescriptor(_, _, _, GATT_WRITE, _, _)).Times(0);
  EXPECT_CALL(gatt_interface, RegisterForNotifications(_, _, _)).Times(0);

  auto chrc_read_cb = [](uint16_t conn_id, tGATT_STATUS status, uint16_t handle, uint16_t len,
                         uint8_t* value, void* data) {};
  auto chrc_multi_read_cb = [](uint16_t conn_id, tGATT_STATUS status, tBTA_GATTC_MULTI& handles,
                               uint16_t len, uint8_t* value, void* data) {};
  auto cccd_write_cb = [](uint16_t conn_id, tGATT_STATUS status, uint16_t handle, uint16_t len,
                          const uint8_t* value, void* data) {};
  device->EnqueueRemainingRequests(gatt_if, chrc_read_cb, chrc_multi_read_cb, cccd_write_cb);
  Mock::VerifyAndClearExpectations(&gatt_queue);
  Mock::VerifyAndClearExpectations(&gatt_interface);
}

TEST_F(VolumeControlDeviceTest, test_enqueue_remaining_requests_multiread) {
  com::android::bluetooth::flags::provider_->le_ase_read_multiple_variable(true);

  SetSampleDatabase1();

  tGATT_IF gatt_if = 0x0001;
  std::vector<uint8_t> register_for_notification_data({0x01, 0x00});

  tBTA_GATTC_MULTI expected_to_read_part_1 = {
          .num_attr = 6,
          .handles = {0x0062 /* audio output state 1 */, 0x0065 /* audio output location 1 */,
                      0x0069 /* audio output description 1 */, 0x0082 /* audio output state 1 */,
                      0x0085 /* audio output location 1 */,
                      0x008a /* audio output description 1 */},
  };

  tBTA_GATTC_MULTI received_to_read_part_1{};

  EXPECT_CALL(gatt_queue, ReadMultiCharacteristic(_, _, _, _))
          .WillOnce(SaveArg<1>(&received_to_read_part_1));

  EXPECT_CALL(gatt_queue, WriteDescriptor(_, _, _, GATT_WRITE, _, _)).Times(0);
  EXPECT_CALL(gatt_interface, RegisterForNotifications(_, _, _)).Times(0);

  auto chrc_read_cb = [](uint16_t conn_id, tGATT_STATUS status, uint16_t handle, uint16_t len,
                         uint8_t* value, void* data) {};
  auto chrc_multi_read_cb = [](uint16_t conn_id, tGATT_STATUS status, tBTA_GATTC_MULTI& handles,
                               uint16_t len, uint8_t* value, void* data) {};
  auto cccd_write_cb = [](uint16_t conn_id, tGATT_STATUS status, uint16_t handle, uint16_t len,
                          const uint8_t* value, void* data) {};
  device->EnqueueRemainingRequests(gatt_if, chrc_read_cb, cccd_write_cb);

  device->EnqueueRemainingRequests(gatt_if, chrc_read_cb, chrc_multi_read_cb, cccd_write_cb);

  Mock::VerifyAndClearExpectations(&gatt_queue);
  Mock::VerifyAndClearExpectations(&gatt_interface);

  ASSERT_EQ(expected_to_read_part_1.num_attr, received_to_read_part_1.num_attr);
}

TEST_F(VolumeControlDeviceTest, test_check_link_encrypted) {
Loading