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

Commit b34e690d authored by David Duarte's avatar David Duarte Committed by Gerrit Code Review
Browse files

Merge changes I26337437,Ie4fd9885,I9c712145,I0e7c78bf into main

* changes:
  hearing_aid: Always update conn params on connect
  hearing_aid: Do a direct connection if another ear is connected
  le_impl: Fix direct connection for device already in acceptlist
  le_impl: Add failing test for direct connection after a background one
parents 1b885617 699ff6ad
Loading
Loading
Loading
Loading
+65 −28
Original line number Diff line number Diff line
@@ -160,6 +160,16 @@ class HearingDevices {
    return (iter == devices.end()) ? nullptr : &(*iter);
  }

  HearingDevice* FindOtherConnectedDeviceFromSet(const HearingDevice& device) {
    auto iter = std::find_if(
        devices.begin(), devices.end(), [&device](const HearingDevice& other) {
          return &device != &other && device.hi_sync_id == other.hi_sync_id &&
                 other.conn_id != 0;
        });

    return (iter == devices.end()) ? nullptr : &(*iter);
  }

  HearingDevice* FindByConnId(uint16_t conn_id) {
    auto iter = std::find_if(devices.begin(), devices.end(),
                             [&conn_id](const HearingDevice& device) {
@@ -178,14 +188,6 @@ class HearingDevices {
    return (iter == devices.end()) ? nullptr : &(*iter);
  }

  bool IsAnyConnectionUpdateStarted() {
    for (const auto& d : devices) {
      if (d.connection_update_status == STARTED) return true;
    }

    return false;
  }

  void StartRssiLog() {
    int read_rssi_start_interval_count = 0;

@@ -468,31 +470,45 @@ class HearingAidImpl : public HearingAid {
        return;
      }

      LOG_INFO("Failed to connect to Hearing Aid device");
      if (hearingDevice->switch_to_background_connection_after_failure) {
        hearingDevice->connecting_actively = false;
        hearingDevice->switch_to_background_connection_after_failure = false;
        BTA_GATTC_Open(gatt_if, address, BTM_BLE_BKG_CONNECT_ALLOW_LIST, false);
      } else {
        LOG_INFO("Failed to connect to Hearing Aid device, bda=%s",
                 ADDRESS_TO_LOGGABLE_CSTR(address));

        hearingDevices.Remove(address);
        callbacks->OnConnectionState(ConnectionState::DISCONNECTED, address);
      }
      return;
    }

    hearingDevice->conn_id = conn_id;

    /* We must update connection parameters one at a time, otherwise anchor
     * point (start of connection event) for two devices can be too close to
     * each other. Here, by setting min_ce_len=max_ce_len=X, we force controller
     * to move anchor point of both connections away from each other, to make
     * sure we'll be able to fit all the data we want in one connection event.
     */
    bool any_update_pending = hearingDevices.IsAnyConnectionUpdateStarted();
    // mark the device as pending connection update. If we don't start the
    // update now, it'll be started once current device finishes.
    if (!any_update_pending) {
      hearingDevice->connection_update_status = STARTED;
      hearingDevice->requested_connection_interval =
          UpdateBleConnParams(address);
    } else {
      hearingDevice->connection_update_status = AWAITING;
    uint64_t hi_sync_id = hearingDevice->hi_sync_id;

    // If there a background connection to the other device of a pair, promote
    // it to a direct connection to scan more agressively for it
    if (hi_sync_id != 0) {
      for (auto& device : hearingDevices.devices) {
        if (device.hi_sync_id == hi_sync_id && device.conn_id == 0 &&
            !device.connecting_actively) {
          LOG_INFO(
              "Promoting device from the set from background to direct"
              "connection, bda=%s",
              ADDRESS_TO_LOGGABLE_CSTR(device.address));
          device.connecting_actively = true;
          device.switch_to_background_connection_after_failure = true;
          BTA_GATTC_Open(gatt_if, device.address, BTM_BLE_DIRECT_CONNECTION,
                         false);
        }
      }
    }

    hearingDevice->connection_update_status = STARTED;
    hearingDevice->requested_connection_interval = UpdateBleConnParams(address);

    if (controller_get_interface()->supports_ble_2m_phy()) {
      LOG_INFO("%s set preferred 2M PHY", ADDRESS_TO_LOGGABLE_CSTR(address));
      BTM_BleSetPhy(address, PHY_LE_2M, PHY_LE_2M, 0);
@@ -1765,10 +1781,31 @@ class HearingAidImpl : public HearingAid {

    DoDisconnectCleanUp(hearingDevice);

    HearingDevice* other_connected_device_from_set =
        hearingDevices.FindOtherConnectedDeviceFromSet(*hearingDevice);

    if (other_connected_device_from_set != nullptr) {
      LOG_INFO(
          "Another device from the set is still connected, issuing a direct "
          "connection, other_device_bda=%s",
          ADDRESS_TO_LOGGABLE_CSTR(other_connected_device_from_set->address));
    }

    // If another device from the pair is still connected, do a direct
    // connection to scan more aggressively and connect as fast as possible
    hearingDevice->connecting_actively =
        other_connected_device_from_set != nullptr;

    auto connection_type = hearingDevice->connecting_actively
                               ? BTM_BLE_DIRECT_CONNECTION
                               : BTM_BLE_BKG_CONNECT_ALLOW_LIST;

    hearingDevice->switch_to_background_connection_after_failure =
        connection_type == BTM_BLE_DIRECT_CONNECTION;

    // This is needed just for the first connection. After stack is restarted,
    // code that loads device will add them to acceptlist.
    BTA_GATTC_Open(gatt_if, hearingDevice->address,
                   BTM_BLE_BKG_CONNECT_ALLOW_LIST, false);
    BTA_GATTC_Open(gatt_if, hearingDevice->address, connection_type, false);

    callbacks->OnConnectionState(ConnectionState::DISCONNECTED, remote_bda);

+5 −1
Original line number Diff line number Diff line
@@ -114,9 +114,11 @@ struct HearingDevice {
  bool service_changed_rcvd;

  /* we are making active attempt to connect to this device, 'direct connect'.
   * This is true only during initial phase of first connection. */
   */
  bool connecting_actively;

  bool switch_to_background_connection_after_failure;

  /* For two hearing aids, you must update their parameters one after another,
   * not simulteanously, to ensure start of connection events for both devices
   * are far from each other. This status tracks whether this device is waiting
@@ -176,6 +178,7 @@ struct HearingDevice {
        first_connection(false),
        service_changed_rcvd(false),
        connecting_actively(false),
        switch_to_background_connection_after_failure(false),
        connection_update_status(NONE),
        accepting_audio(false),
        conn_id(0),
@@ -202,6 +205,7 @@ struct HearingDevice {
        first_connection(first_connection),
        service_changed_rcvd(false),
        connecting_actively(first_connection),
        switch_to_background_connection_after_failure(false),
        connection_update_status(NONE),
        accepting_audio(false),
        conn_id(0),
+21 −5
Original line number Diff line number Diff line
@@ -354,6 +354,11 @@ struct le_impl : public bluetooth::hci::LeAddressManagerCallback {
        on_le_connection_canceled_on_pause();
        return;
      }
      if (status == ErrorCode::UNKNOWN_CONNECTION && arm_on_disarm_) {
        arm_on_disarm_ = false;
        arm_connectability();
        return;
      }
      on_common_le_connection_complete(remote_address);
      if (status == ErrorCode::UNKNOWN_CONNECTION) {
        if (remote_address.GetAddress() != Address::kEmpty) {
@@ -1021,9 +1026,13 @@ struct le_impl : public bluetooth::hci::LeAddressManagerCallback {
      return;
    }

    bool already_in_connect_list = connect_list.find(address_with_type) != connect_list.end();
    // TODO: Configure default LE connection parameters?
    if (add_to_connect_list) {
      if (!already_in_connect_list) {
        add_device_to_connect_list(address_with_type);
      }

      if (is_direct) {
        direct_connections_.insert(address_with_type);
        if (create_connection_timeout_alarms_.find(address_with_type) == create_connection_timeout_alarms_.end()) {
@@ -1060,10 +1069,16 @@ struct le_impl : public bluetooth::hci::LeAddressManagerCallback {
    switch (connectability_state_) {
      case ConnectabilityState::ARMED:
      case ConnectabilityState::ARMING:
        // Ignored, if we add new device to the filter accept list, create connection command will be sent by OnResume.
        if (already_in_connect_list) {
          arm_on_disarm_ = true;
          disarm_connectability();
        } else {
          // Ignored, if we add new device to the filter accept list, create connection command will
          // be sent by OnResume.
          LOG_DEBUG(
              "Deferred until filter accept list updated create connection state %s",
              connectability_state_machine_text(connectability_state_).c_str());
        }
        break;
      default:
        // If we added to filter accept list then the arming of the le state machine
@@ -1299,6 +1314,7 @@ struct le_impl : public bluetooth::hci::LeAddressManagerCallback {
  LeAcceptlistCallbacks* le_acceptlist_callbacks_ = nullptr;
  std::unordered_set<AddressWithType> connecting_le_{};
  bool arm_on_resume_{};
  bool arm_on_disarm_{};
  std::unordered_set<AddressWithType> direct_connections_{};
  // Set of devices that will not be removed from connect list after direct connect timeout
  std::unordered_set<AddressWithType> background_connections_;
+70 −8
Original line number Diff line number Diff line
@@ -321,7 +321,7 @@ class TestHciLayer : public HciLayer {
    return CommandView::Create(GetPacketView(std::move(last)));
  }

  CommandView GetCommand(OpCode op_code) {
  std::optional<CommandView> GetCommandOptional(OpCode op_code) {
    if (!command_queue_.empty()) {
      std::lock_guard<std::mutex> lock(command_queue_mutex_);
      if (command_future_ != nullptr) {
@@ -329,17 +329,27 @@ class TestHciLayer : public HciLayer {
        command_promise_.reset();
      }
    } else if (command_future_ != nullptr) {
      auto result = command_future_->wait_for(std::chrono::milliseconds(1000));
      EXPECT_NE(std::future_status::timeout, result);
      command_future_->wait_for(std::chrono::milliseconds(1000));
    }
    std::lock_guard<std::mutex> lock(command_queue_mutex_);
    ASSERT_LOG(
        !command_queue_.empty(), "Expecting command %s but command queue was empty", OpCodeText(op_code).c_str());
    if (command_queue_.empty()) {
      return std::nullopt;
    } else {
      CommandView command_packet_view = GetLastCommand();
      EXPECT_TRUE(command_packet_view.IsValid());
      EXPECT_EQ(command_packet_view.GetOpCode(), op_code);
      return command_packet_view;
    }
  }

  CommandView GetCommand(OpCode op_code) {
    std::optional<CommandView> command = GetCommandOptional(op_code);
    ASSERT_LOG(
        command.has_value(),
        "Expecting command %s but command queue was empty",
        OpCodeText(op_code).c_str());
    return command.value();
  }

  void CommandCompleteCallback(std::unique_ptr<EventBuilder> event_builder) {
    auto event = EventView::Create(GetPacketView(std::move(event_builder)));
@@ -1811,6 +1821,58 @@ TEST_F(LeImplTest, DisconnectionAcceptlistCallback) {
  Mock::VerifyAndClearExpectations(&callbacks);
}

TEST_F(LeImplTest, direct_connection_after_background_connection) {
  set_random_device_address_policy();

  hci::AddressWithType address(
      {0x21, 0x22, 0x23, 0x24, 0x25, 0x26}, AddressType::PUBLIC_DEVICE_ADDRESS);

  // arrange: Create background connection
  ASSERT_NO_FATAL_FAILURE(hci_layer_->SetCommandFuture());
  le_impl_->create_le_connection(address, true, /* is_direct */ false);
  hci_layer_->GetCommand(OpCode::LE_ADD_DEVICE_TO_FILTER_ACCEPT_LIST);
  ASSERT_NO_FATAL_FAILURE(hci_layer_->SetCommandFuture());
  hci_layer_->CommandCompleteCallback(
      LeAddDeviceToFilterAcceptListCompleteBuilder::Create(0x01, ErrorCode::SUCCESS));
  auto raw_bg_create_connection = hci_layer_->GetCommand(OpCode::LE_CREATE_CONNECTION);
  hci_layer_->CommandStatusCallback(
      LeCreateConnectionStatusBuilder::Create(ErrorCode::SUCCESS, 0x01));
  sync_handler();

  // act: Create direct connection
  ASSERT_NO_FATAL_FAILURE(hci_layer_->SetCommandFuture());
  le_impl_->create_le_connection(address, true, /* is_direct */ true);
  auto cancel_connection = hci_layer_->GetCommandOptional(OpCode::LE_CREATE_CONNECTION_CANCEL);
  if (cancel_connection) {
    ASSERT_NO_FATAL_FAILURE(hci_layer_->SetCommandFuture());
    hci_layer_->CommandCompleteCallback(
        LeCreateConnectionCancelCompleteBuilder::Create(0x01, ErrorCode::SUCCESS));
    hci_layer_->IncomingLeMetaEvent(LeConnectionCompleteBuilder::Create(
        ErrorCode::UNKNOWN_CONNECTION,
        kHciHandle,
        Role::CENTRAL,
        AddressType::PUBLIC_DEVICE_ADDRESS,
        Address::kEmpty,
        0x0000,
        0x0000,
        0x0000,
        ClockAccuracy::PPM_30));
  }
  auto raw_direct_create_connection = hci_layer_->GetCommandOptional(OpCode::LE_CREATE_CONNECTION);

  // assert
  ASSERT_TRUE(raw_direct_create_connection.has_value());
  auto bg_create_connection = LeCreateConnectionView::Create(
      LeConnectionManagementCommandView::Create(AclCommandView::Create(raw_bg_create_connection)));
  EXPECT_TRUE(bg_create_connection.IsValid());
  auto direct_create_connection =
      LeCreateConnectionView::Create(LeConnectionManagementCommandView::Create(
          AclCommandView::Create(*raw_direct_create_connection)));
  EXPECT_TRUE(direct_create_connection.IsValid());
  LOG_INFO("Scan Interval %u", direct_create_connection.GetLeScanInterval());
  ASSERT_NE(direct_create_connection.GetLeScanInterval(), bg_create_connection.GetLeScanInterval());
}

}  // namespace acl_manager
}  // namespace hci
}  // namespace bluetooth