| /* |
| * Copyright 2020 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "hci/acl_manager/le_acl_connection.h" |
| #include "hci/acl_manager/le_connection_management_callbacks.h" |
| #include "os/metrics.h" |
| |
| namespace bluetooth { |
| namespace hci { |
| namespace acl_manager { |
| |
| class LeAclConnectionTracker : public LeConnectionManagementCallbacks { |
| public: |
| LeAclConnectionTracker(LeAclConnectionInterface* le_acl_connection_interface, uint16_t connection_handle) |
| : le_acl_connection_interface_(le_acl_connection_interface), connection_handle_(connection_handle) {} |
| ~LeAclConnectionTracker() { |
| ASSERT(queued_callbacks_.empty()); |
| } |
| void RegisterCallbacks(LeConnectionManagementCallbacks* callbacks, os::Handler* handler) { |
| client_handler_ = handler; |
| client_callbacks_ = callbacks; |
| while (!queued_callbacks_.empty()) { |
| auto iter = queued_callbacks_.begin(); |
| handler->Post(std::move(*iter)); |
| queued_callbacks_.erase(iter); |
| } |
| } |
| |
| #define SAVE_OR_CALL(f, ...) \ |
| if (client_handler_ == nullptr) { \ |
| queued_callbacks_.emplace_back( \ |
| common::BindOnce(&LeConnectionManagementCallbacks::f, common::Unretained(this), __VA_ARGS__)); \ |
| } else { \ |
| client_handler_->Post( \ |
| common::BindOnce(&LeConnectionManagementCallbacks::f, common::Unretained(client_callbacks_), __VA_ARGS__)); \ |
| } |
| |
| void OnConnectionUpdate( |
| hci::ErrorCode hci_status, uint16_t conn_interval, uint16_t conn_latency, uint16_t supervision_timeout) override { |
| SAVE_OR_CALL(OnConnectionUpdate, hci_status, conn_interval, conn_latency, supervision_timeout) |
| } |
| |
| void OnDataLengthChange(uint16_t tx_octets, uint16_t tx_time, uint16_t rx_octets, uint16_t rx_time) override { |
| SAVE_OR_CALL(OnDataLengthChange, tx_octets, tx_time, rx_octets, rx_time) |
| } |
| |
| void OnReadRemoteVersionInformationComplete( |
| hci::ErrorCode hci_status, uint8_t lmp_version, uint16_t manufacturer_name, uint16_t sub_version) { |
| bluetooth::os::LogMetricRemoteVersionInfo( |
| connection_handle_, static_cast<uint8_t>(hci_status), lmp_version, manufacturer_name, sub_version); |
| SAVE_OR_CALL(OnReadRemoteVersionInformationComplete, hci_status, lmp_version, manufacturer_name, sub_version); |
| } |
| void OnPhyUpdate(hci::ErrorCode hci_status, uint8_t tx_phy, uint8_t rx_phy) override { |
| SAVE_OR_CALL(OnPhyUpdate, hci_status, tx_phy, rx_phy); |
| } |
| void OnLocalAddressUpdate(AddressWithType address_with_type) override { |
| SAVE_OR_CALL(OnLocalAddressUpdate, address_with_type); |
| } |
| |
| void OnDisconnection(ErrorCode reason) override { |
| SAVE_OR_CALL(OnDisconnection, reason); |
| } |
| #undef SAVE_OR_CALL |
| |
| LeAclConnectionInterface* le_acl_connection_interface_; |
| os::Handler* client_handler_ = nullptr; |
| LeConnectionManagementCallbacks* client_callbacks_ = nullptr; |
| std::list<common::OnceClosure> queued_callbacks_; |
| uint16_t connection_handle_; |
| }; |
| |
| struct LeAclConnection::impl { |
| impl(LeAclConnectionInterface* le_acl_connection_interface, std::shared_ptr<Queue> queue, uint16_t connection_handle) |
| : queue_(std::move(queue)), tracker(le_acl_connection_interface, connection_handle) {} |
| LeConnectionManagementCallbacks* GetEventCallbacks() { |
| ASSERT(!callbacks_given_); |
| callbacks_given_ = true; |
| return &tracker; |
| } |
| |
| bool callbacks_given_{false}; |
| std::shared_ptr<Queue> queue_; |
| LeAclConnectionTracker tracker; |
| }; |
| |
| LeAclConnection::LeAclConnection() |
| : AclConnection(), local_address_(Address::kEmpty, AddressType::PUBLIC_DEVICE_ADDRESS), |
| remote_address_(Address::kEmpty, AddressType::PUBLIC_DEVICE_ADDRESS) {} |
| |
| LeAclConnection::LeAclConnection( |
| std::shared_ptr<Queue> queue, |
| LeAclConnectionInterface* le_acl_connection_interface, |
| uint16_t handle, |
| AddressWithType local_address, |
| AddressWithType remote_address, |
| Role role) |
| : AclConnection(queue->GetUpEnd(), handle), |
| local_address_(local_address), |
| remote_address_(remote_address), |
| role_(role) { |
| pimpl_ = new LeAclConnection::impl(le_acl_connection_interface, std::move(queue), handle); |
| } |
| |
| LeAclConnection::~LeAclConnection() { |
| delete pimpl_; |
| } |
| |
| void LeAclConnection::RegisterCallbacks(LeConnectionManagementCallbacks* callbacks, os::Handler* handler) { |
| return pimpl_->tracker.RegisterCallbacks(callbacks, handler); |
| } |
| |
| void LeAclConnection::Disconnect(DisconnectReason reason) { |
| pimpl_->tracker.le_acl_connection_interface_->EnqueueCommand( |
| DisconnectBuilder::Create(handle_, reason), |
| pimpl_->tracker.client_handler_->BindOnce([](CommandStatusView status) { |
| ASSERT(status.IsValid()); |
| ASSERT(status.GetCommandOpCode() == OpCode::DISCONNECT); |
| auto disconnect_status = DisconnectStatusView::Create(status); |
| ASSERT(disconnect_status.IsValid()); |
| auto error_code = disconnect_status.GetStatus(); |
| if (error_code != ErrorCode::SUCCESS) { |
| LOG_INFO("Disconnect status %s", ErrorCodeText(error_code).c_str()); |
| } |
| })); |
| } |
| |
| LeConnectionManagementCallbacks* LeAclConnection::GetEventCallbacks() { |
| return pimpl_->GetEventCallbacks(); |
| } |
| |
| bool LeAclConnection::LeConnectionUpdate(uint16_t conn_interval_min, uint16_t conn_interval_max, uint16_t conn_latency, |
| uint16_t supervision_timeout, uint16_t min_ce_length, uint16_t max_ce_length) { |
| if (!check_connection_parameters(conn_interval_min, conn_interval_max, conn_latency, supervision_timeout)) { |
| LOG_ERROR("Invalid parameter"); |
| return false; |
| } |
| pimpl_->tracker.le_acl_connection_interface_->EnqueueCommand( |
| LeConnectionUpdateBuilder::Create(handle_, conn_interval_min, conn_interval_max, conn_latency, |
| supervision_timeout, min_ce_length, max_ce_length), |
| pimpl_->tracker.client_handler_->BindOnce([](CommandStatusView status) { |
| ASSERT(status.IsValid()); |
| ASSERT(status.GetCommandOpCode() == OpCode::LE_CONNECTION_UPDATE); |
| })); |
| return true; |
| } |
| |
| bool LeAclConnection::ReadRemoteVersionInformation() { |
| pimpl_->tracker.le_acl_connection_interface_->EnqueueCommand( |
| ReadRemoteVersionInformationBuilder::Create(handle_), |
| pimpl_->tracker.client_handler_->BindOnce([](CommandStatusView status) { |
| ASSERT(status.IsValid()); |
| ASSERT(status.GetCommandOpCode() == OpCode::READ_REMOTE_VERSION_INFORMATION); |
| })); |
| return true; |
| } |
| |
| bool LeAclConnection::check_connection_parameters( |
| uint16_t conn_interval_min, uint16_t conn_interval_max, uint16_t conn_latency, uint16_t supervision_timeout) { |
| 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; |
| } |
| // The Maximum interval in milliseconds will be conn_interval_max * 1.25 ms |
| // The Timeout in milliseconds will be expected_supervision_timeout * 10 ms |
| // The Timeout in milliseconds shall be larger than (1 + Latency) * Interval_Max * 2, where Interval_Max is given in |
| // milliseconds. |
| uint32_t supervision_timeout_min = (uint32_t)(1 + conn_latency) * conn_interval_max * 2 + 1; |
| if (supervision_timeout * 8 < supervision_timeout_min || conn_interval_max < conn_interval_min) { |
| LOG_ERROR("Invalid parameter"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| } // namespace acl_manager |
| } // namespace hci |
| } // namespace bluetooth |