os::Handler* disconnect_handler_ = nullptr;
ConnectionManagementCallbacks* command_complete_callbacks_;
common::OnceCallback<void(ErrorCode)> on_disconnect_callback_;
+ // For LE Connection parameter update from L2CAP
+ common::OnceCallback<void(ErrorCode)> on_connection_update_complete_callback_;
+ os::Handler* on_connection_update_complete_callback_handler_ = nullptr;
// Round-robin: Track if dequeue is registered for this connection
bool is_registered_ = false;
// Credits: Track the number of packets which have been sent to the controller
hci_layer_->RegisterLeEventHandler(SubeventCode::ENHANCED_CONNECTION_COMPLETE,
Bind(&impl::on_le_enhanced_connection_complete, common::Unretained(this)),
handler_);
+ hci_layer_->RegisterLeEventHandler(SubeventCode::CONNECTION_UPDATE_COMPLETE,
+ Bind(&impl::on_le_connection_update_complete, common::Unretained(this)),
+ handler_);
hci_layer_->RegisterEventHandler(EventCode::CONNECTION_PACKET_TYPE_CHANGED,
Bind(&impl::on_connection_packet_type_changed, common::Unretained(this)),
handler_);
}
}
+ void on_le_connection_update_complete(LeMetaEventView view) {
+ auto complete_view = LeConnectionUpdateCompleteView::Create(view);
+ if (!complete_view.IsValid()) {
+ LOG_ERROR("Received on_le_connection_update_complete with invalid packet");
+ return;
+ } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+ auto status = complete_view.GetStatus();
+ std::string error_code = ErrorCodeText(status);
+ LOG_ERROR("Received on_le_connection_update_complete with error code %s", error_code.c_str());
+ return;
+ }
+ auto handle = complete_view.GetConnectionHandle();
+ if (acl_connections_.find(handle) == acl_connections_.end()) {
+ LOG_WARN("Can't find connection");
+ return;
+ }
+ auto& connection = acl_connections_.find(handle)->second;
+ if (connection.is_disconnected_) {
+ LOG_INFO("Already disconnected");
+ return;
+ }
+ if (!connection.on_connection_update_complete_callback_.is_null()) {
+ connection.on_connection_update_complete_callback_handler_->Post(
+ common::BindOnce(std::move(connection.on_connection_update_complete_callback_), complete_view.GetStatus()));
+ connection.on_connection_update_complete_callback_handler_ = nullptr;
+ }
+ }
+
bool is_classic_link_already_connected(Address address) {
for (const auto& connection : acl_connections_) {
if (connection.second.address_with_type_.GetAddress() == address) {
uint16_t conn_interval_max = 0x0C00;
uint16_t conn_latency = 0x0C0;
uint16_t supervision_timeout = 0x0C00;
- uint16_t minimum_ce_length = 0x0002;
- uint16_t maximum_ce_length = 0x0C00;
ASSERT(le_client_callbacks_ != nullptr);
connecting_le_.insert(address_with_type);
LeCreateConnectionBuilder::Create(le_scan_interval, le_scan_window, initiator_filter_policy,
address_with_type.GetAddressType(), address_with_type.GetAddress(),
own_address_type, conn_interval_min, conn_interval_max, conn_latency,
- supervision_timeout, minimum_ce_length, maximum_ce_length),
+ supervision_timeout, kMinimumCeLength, kMaximumCeLength),
common::BindOnce([](CommandStatusView status) {
ASSERT(status.IsValid());
ASSERT(status.GetCommandOpCode() == OpCode::LE_CREATE_CONNECTION);
common::BindOnce(&impl::on_read_clock_complete, common::Unretained(this)), handler_);
}
+ void handle_le_connection_update(uint16_t handle, uint16_t conn_interval_min, uint16_t conn_interval_max,
+ uint16_t conn_latency, uint16_t supervision_timeout) {
+ auto packet = LeConnectionUpdateBuilder::Create(handle, conn_interval_min, conn_interval_max, conn_latency,
+ supervision_timeout, kMinimumCeLength, kMaximumCeLength);
+ hci_layer_->EnqueueCommand(std::move(packet), common::BindOnce([](CommandStatusView status) {
+ ASSERT(status.IsValid());
+ ASSERT(status.GetCommandOpCode() == OpCode::LE_CREATE_CONNECTION);
+ }),
+ handler_);
+ }
+
template <class T>
void check_command_complete(CommandCompleteView view) {
ASSERT(view.IsValid());
return true;
}
+ bool LeConnectionUpdate(uint16_t handle, uint16_t conn_interval_min, uint16_t conn_interval_max,
+ uint16_t conn_latency, uint16_t supervision_timeout,
+ common::OnceCallback<void(ErrorCode)> done_callback, os::Handler* handler) {
+ auto& connection = check_and_get_connection(handle);
+ if (connection.is_disconnected_) {
+ LOG_INFO("Already disconnected");
+ return false;
+ }
+ if (!connection.on_connection_update_complete_callback_.is_null()) {
+ LOG_INFO("There is another pending connection update");
+ return false;
+ }
+ connection.on_connection_update_complete_callback_ = std::move(done_callback);
+ connection.on_connection_update_complete_callback_handler_ = handler;
+ if (conn_interval_min < 0x0006 || conn_interval_min > 0x0C80 || conn_interval_max < 0x0006 ||
+ conn_interval_max > 0x0C80 || conn_latency > 0x01F3 || supervision_timeout < 0x000A ||
+ supervision_timeout > 0x0C80) {
+ LOG_ERROR("Invalid parameter");
+ return false;
+ }
+ handler_->Post(BindOnce(&impl::handle_le_connection_update, common::Unretained(this), handle, conn_interval_min,
+ conn_interval_max, conn_latency, supervision_timeout));
+ return true;
+ }
+
void Finish(uint16_t handle) {
auto& connection = check_and_get_connection(handle);
ASSERT_LOG(connection.is_disconnected_, "Finish must be invoked after disconnection (handle 0x%04hx)", handle);
const AclManager& acl_manager_;
+ static constexpr uint16_t kMinimumCeLength = 0x0002;
+ static constexpr uint16_t kMaximumCeLength = 0x0C00;
+
Controller* controller_ = nullptr;
uint16_t max_acl_packet_credits_ = 0;
uint16_t acl_packet_credits_ = 0;
return manager_->pimpl_->ReadClock(handle_, which_clock);
}
+bool AclConnection::LeConnectionUpdate(uint16_t conn_interval_min, uint16_t conn_interval_max, uint16_t conn_latency,
+ uint16_t supervision_timeout,
+ common::OnceCallback<void(ErrorCode)> done_callback, os::Handler* handler) {
+ return manager_->pimpl_->LeConnectionUpdate(handle_, conn_interval_min, conn_interval_max, conn_latency,
+ supervision_timeout, std::move(done_callback), handler);
+}
+
void AclConnection::Finish() {
return manager_->pimpl_->Finish(handle_);
}
0x0100, 0x0010, 0x0011, MasterClockAccuracy::PPM_30));
}
+TEST_F(AclManagerTest, invoke_registered_callback_le_connection_update_success) {
+ AddressWithType remote_with_type(remote, AddressType::PUBLIC_DEVICE_ADDRESS);
+ test_hci_layer_->SetCommandFuture();
+ acl_manager_->CreateLeConnection(remote_with_type);
+
+ auto packet = test_hci_layer_->GetCommandPacket(OpCode::LE_CREATE_CONNECTION);
+ auto le_connection_management_command_view = LeConnectionManagementCommandView::Create(packet);
+ auto command_view = LeCreateConnectionView::Create(le_connection_management_command_view);
+ ASSERT(command_view.IsValid());
+ EXPECT_EQ(command_view.GetPeerAddress(), remote);
+ EXPECT_EQ(command_view.GetPeerAddressType(), AddressType::PUBLIC_DEVICE_ADDRESS);
+
+ test_hci_layer_->IncomingEvent(LeCreateConnectionStatusBuilder::Create(ErrorCode::SUCCESS, 0x01));
+
+ auto first_connection = GetLeConnectionFuture();
+
+ test_hci_layer_->IncomingLeMetaEvent(
+ LeConnectionCompleteBuilder::Create(ErrorCode::SUCCESS, 0x123, Role::SLAVE, AddressType::PUBLIC_DEVICE_ADDRESS,
+ remote, 0x0100, 0x0010, 0x0011, MasterClockAccuracy::PPM_30));
+
+ auto first_connection_status = first_connection.wait_for(kTimeout);
+ ASSERT_EQ(first_connection_status, std::future_status::ready);
+
+ std::shared_ptr<AclConnection> connection = GetLastLeConnection();
+ ASSERT_EQ(connection->GetAddress(), remote);
+
+ std::promise<ErrorCode> promise;
+ auto future = promise.get_future();
+ connection->LeConnectionUpdate(
+ 0x0006, 0x0C80, 0x0000, 0x000A,
+ common::BindOnce([](std::promise<ErrorCode> promise, ErrorCode code) { promise.set_value(code); },
+ std::move(promise)),
+ client_handler_);
+ test_hci_layer_->IncomingLeMetaEvent(
+ LeConnectionUpdateCompleteBuilder::Create(ErrorCode::SUCCESS, 0x123, 0x0006, 0x0000, 0x000A));
+ EXPECT_EQ(future.wait_for(std::chrono::milliseconds(3)), std::future_status::ready);
+ EXPECT_EQ(future.get(), ErrorCode::SUCCESS);
+}
+
TEST_F(AclManagerTest, invoke_registered_callback_disconnection_complete) {
uint16_t handle = 0x123;
acl_connection_->Disconnect(hci::DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION);
}
+void Link::UpdateConnectionParameter(SignalId signal_id, uint16_t conn_interval_min, uint16_t conn_interval_max,
+ uint16_t conn_latency, uint16_t supervision_timeout) {
+ acl_connection_->LeConnectionUpdate(
+ conn_interval_min, conn_interval_max, conn_latency, supervision_timeout,
+ common::BindOnce(&Link::on_connection_update_complete, common::Unretained(this), signal_id), l2cap_handler_);
+}
+
std::shared_ptr<FixedChannelImpl> Link::AllocateFixedChannel(Cid cid, SecurityPolicy security_policy) {
auto channel = fixed_channel_allocator_.AllocateChannel(cid, security_policy);
data_pipeline_manager_.AttachChannel(cid, channel,
return parameter_provider_->GetLeInitialCredit();
}
-
void Link::SendLeCredit(Cid local_cid, uint16_t credit) {
signalling_manager_.SendCredit(local_cid, credit);
}
+void Link::on_connection_update_complete(SignalId signal_id, hci::ErrorCode error_code) {
+ ConnectionParameterUpdateResponseResult result = (error_code == hci::ErrorCode::SUCCESS)
+ ? ConnectionParameterUpdateResponseResult::ACCEPTED
+ : ConnectionParameterUpdateResponseResult::REJECTED;
+ signalling_manager_.SendConnectionParameterUpdateResponse(SignalId(), result);
+}
+
} // namespace internal
} // namespace le
} // namespace l2cap