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

Commit 7afee0ee authored by Łukasz Rymanowski's avatar Łukasz Rymanowski
Browse files

vc: Improve initial volume control operations

With this patch initial operations contains only VCS mandatory
characteristics plus all CCC subscriptions (this is topic to improvement
as well).

After profile is notified to Java that it is connected, native schedull
remaining reads which is all the VOCS reads.

Bug: 331775328
Test: atest bluetooth_vc_test
Flags: EXEMPT, regression covered with unit tests.
Change-Id: Ic365bf8bdeea49ba33a564e8b5dc18d6509f6a46
parent ed1ac115
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