Merge "LE ACL: Expose Connection Update API to L2CAP"
diff --git a/gd/hci/acl_manager.cc b/gd/hci/acl_manager.cc
index faad20b..4e337ff 100644
--- a/gd/hci/acl_manager.cc
+++ b/gd/hci/acl_manager.cc
@@ -46,6 +46,9 @@
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
@@ -84,6 +87,9 @@
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_);
@@ -864,6 +870,34 @@
}
}
+ 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) {
@@ -911,8 +945,6 @@
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);
@@ -921,7 +953,7 @@
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);
@@ -1188,6 +1220,17 @@
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());
@@ -1574,6 +1617,31 @@
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);
@@ -1582,6 +1650,9 @@
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;
@@ -1732,6 +1803,13 @@
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_);
}
diff --git a/gd/hci/acl_manager.h b/gd/hci/acl_manager.h
index 3706aba..1402628 100644
--- a/gd/hci/acl_manager.h
+++ b/gd/hci/acl_manager.h
@@ -143,6 +143,11 @@
virtual bool ReadRssi();
virtual bool ReadClock(WhichClock which_clock);
+ // LE ACL Method
+ virtual bool 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);
+
// Ask AclManager to clean me up. Must invoke after on_disconnect is called
virtual void Finish();
diff --git a/gd/hci/acl_manager_test.cc b/gd/hci/acl_manager_test.cc
index 805f926..50c4e05 100644
--- a/gd/hci/acl_manager_test.cc
+++ b/gd/hci/acl_manager_test.cc
@@ -537,6 +537,45 @@
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;
diff --git a/gd/l2cap/le/internal/link.cc b/gd/l2cap/le/internal/link.cc
index 66dd580..2f147a7 100644
--- a/gd/l2cap/le/internal/link.cc
+++ b/gd/l2cap/le/internal/link.cc
@@ -55,6 +55,13 @@
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,
@@ -172,11 +179,17 @@
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
diff --git a/gd/l2cap/le/internal/link.h b/gd/l2cap/le/internal/link.h
index 633aa29..1d4ed3a 100644
--- a/gd/l2cap/le/internal/link.h
+++ b/gd/l2cap/le/internal/link.h
@@ -67,6 +67,10 @@
virtual void Disconnect();
+ // Handles connection parameter update request from remote
+ virtual void UpdateConnectionParameter(SignalId signal_id, uint16_t conn_interval_min, uint16_t conn_interval_max,
+ uint16_t conn_latency, uint16_t supervision_timeout);
+
// FixedChannel methods
virtual std::shared_ptr<FixedChannelImpl> AllocateFixedChannel(Cid cid, SecurityPolicy security_policy);
@@ -122,6 +126,8 @@
std::unordered_map<Cid, PendingDynamicChannelConnection> local_cid_to_pending_dynamic_channel_connection_map_;
os::Alarm link_idle_disconnect_alarm_{l2cap_handler_};
DISALLOW_COPY_AND_ASSIGN(Link);
+
+ void on_connection_update_complete(SignalId signal_id, hci::ErrorCode error_code);
};
} // namespace internal
diff --git a/gd/l2cap/le/internal/signalling_manager.cc b/gd/l2cap/le/internal/signalling_manager.cc
index 18b2e95..2922a74 100644
--- a/gd/l2cap/le/internal/signalling_manager.cc
+++ b/gd/l2cap/le/internal/signalling_manager.cc
@@ -81,8 +81,10 @@
LOG_ERROR("Not implemented");
}
-void LeSignallingManager::SendConnectionParameterUpdateResponse(ConnectionParameterUpdateResponseResult result) {
- LOG_ERROR("Not implemented");
+void LeSignallingManager::SendConnectionParameterUpdateResponse(SignalId signal_id,
+ ConnectionParameterUpdateResponseResult result) {
+ auto builder = ConnectionParameterUpdateResponseBuilder::Create(signal_id.Value(), result);
+ enqueue_buffer_->Enqueue(std::move(builder), handler_);
}
void LeSignallingManager::SendCredit(Cid local_cid, uint16_t credits) {
diff --git a/gd/l2cap/le/internal/signalling_manager.h b/gd/l2cap/le/internal/signalling_manager.h
index c93812b..74e85ce 100644
--- a/gd/l2cap/le/internal/signalling_manager.h
+++ b/gd/l2cap/le/internal/signalling_manager.h
@@ -68,7 +68,7 @@
void SendConnectionParameterUpdateRequest(uint16_t interval_min, uint16_t interval_max, uint16_t slave_latency,
uint16_t timeout_multiplier);
- void SendConnectionParameterUpdateResponse(ConnectionParameterUpdateResponseResult result);
+ void SendConnectionParameterUpdateResponse(SignalId signal_id, ConnectionParameterUpdateResponseResult result);
void SendCredit(Cid local_cid, uint16_t credits);