| /* |
| * 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. |
| */ |
| |
| #pragma once |
| |
| #include "common/bind.h" |
| #include "crypto_toolbox/crypto_toolbox.h" |
| #include "hci/acl_manager/assembler.h" |
| #include "hci/acl_manager/round_robin_scheduler.h" |
| #include "hci/le_address_manager.h" |
| #include "os/alarm.h" |
| |
| using bluetooth::crypto_toolbox::Octet16; |
| |
| namespace bluetooth { |
| namespace hci { |
| namespace acl_manager { |
| |
| using common::BindOnce; |
| |
| constexpr uint16_t kScanIntervalFast = 0x0060; /* 30 ~ 60 ms (use 60) = 96 *0.625 */ |
| constexpr uint16_t kScanWindowFast = 0x0030; /* 30 ms = 48 *0.625 */ |
| constexpr uint16_t kScanWindow2mFast = 0x0018; /* 15 ms = 24 *0.625 */ |
| constexpr uint16_t kScanWindowCodedFast = 0x0018; /* 15 ms = 24 *0.625 */ |
| constexpr uint16_t kScanIntervalSlow = 0x0800; /* 1.28 s = 2048 *0.625 */ |
| constexpr uint16_t kScanWindowSlow = 0x0030; /* 30 ms = 48 *0.625 */ |
| constexpr std::chrono::milliseconds kCreateConnectionTimeoutMs = std::chrono::milliseconds(30 * 1000); |
| constexpr uint8_t PHY_LE_NO_PACKET = 0x00; |
| constexpr uint8_t PHY_LE_1M = 0x01; |
| constexpr uint8_t PHY_LE_2M = 0x02; |
| constexpr uint8_t PHY_LE_CODED = 0x04; |
| |
| struct le_acl_connection { |
| le_acl_connection(AddressWithType remote_address, AclConnection::QueueDownEnd* queue_down_end, os::Handler* handler) |
| : assembler_(remote_address, queue_down_end, handler), remote_address_(remote_address) {} |
| ~le_acl_connection() = default; |
| struct acl_manager::assembler assembler_; |
| AddressWithType remote_address_; |
| LeConnectionManagementCallbacks* le_connection_management_callbacks_ = nullptr; |
| }; |
| |
| struct le_impl : public bluetooth::hci::LeAddressManagerCallback { |
| le_impl( |
| HciLayer* hci_layer, |
| Controller* controller, |
| os::Handler* handler, |
| RoundRobinScheduler* round_robin_scheduler, |
| bool crash_on_unknown_handle) |
| : hci_layer_(hci_layer), |
| controller_(controller), |
| round_robin_scheduler_(round_robin_scheduler), |
| crash_on_unknown_handle_(crash_on_unknown_handle) { |
| hci_layer_ = hci_layer; |
| controller_ = controller; |
| handler_ = handler; |
| le_acl_connection_interface_ = hci_layer_->GetLeAclConnectionInterface( |
| handler_->BindOn(this, &le_impl::on_le_event), |
| handler_->BindOn(this, &le_impl::on_le_disconnect), |
| handler_->BindOn(this, &le_impl::on_le_read_remote_version_information)); |
| le_address_manager_ = new LeAddressManager( |
| common::Bind(&le_impl::enqueue_command, common::Unretained(this)), |
| handler_, |
| controller->GetMacAddress(), |
| controller->GetLeConnectListSize(), |
| controller->GetLeResolvingListSize()); |
| } |
| |
| ~le_impl() { |
| for (auto subevent_code : LeConnectionManagementEvents) { |
| hci_layer_->UnregisterLeEventHandler(subevent_code); |
| } |
| if (address_manager_registered) { |
| le_address_manager_->Unregister(this); |
| } |
| delete le_address_manager_; |
| le_acl_connections_.clear(); |
| } |
| |
| void on_le_event(LeMetaEventView event_packet) { |
| SubeventCode code = event_packet.GetSubeventCode(); |
| switch (code) { |
| case SubeventCode::CONNECTION_COMPLETE: |
| on_le_connection_complete(event_packet); |
| break; |
| case SubeventCode::ENHANCED_CONNECTION_COMPLETE: |
| on_le_enhanced_connection_complete(event_packet); |
| break; |
| case SubeventCode::CONNECTION_UPDATE_COMPLETE: |
| on_le_connection_update_complete(event_packet); |
| break; |
| case SubeventCode::PHY_UPDATE_COMPLETE: |
| on_le_phy_update_complete(event_packet); |
| break; |
| case SubeventCode::DATA_LENGTH_CHANGE: |
| on_data_length_change(event_packet); |
| break; |
| case SubeventCode::REMOTE_CONNECTION_PARAMETER_REQUEST: |
| on_remote_connection_parameter_request(event_packet); |
| break; |
| default: |
| LOG_ALWAYS_FATAL("Unhandled event code %s", SubeventCodeText(code).c_str()); |
| } |
| } |
| |
| LeConnectionManagementCallbacks* get_callbacks(uint16_t handle) { |
| auto connection = le_acl_connections_.find(handle); |
| if (connection == le_acl_connections_.end()) { |
| return nullptr; |
| } |
| return connection->second.le_connection_management_callbacks_; |
| } |
| |
| void on_le_disconnect(uint16_t handle, ErrorCode reason) { |
| auto callbacks = get_callbacks(handle); |
| if (callbacks == nullptr) { |
| return; |
| } |
| round_robin_scheduler_->Unregister(handle); |
| callbacks->OnDisconnection(reason); |
| le_acl_connections_.erase(handle); |
| } |
| |
| void on_common_le_connection_complete(AddressWithType address_with_type) { |
| auto connecting_addr_with_type = connecting_le_.find(address_with_type); |
| if (connecting_addr_with_type == connecting_le_.end()) { |
| LOG_WARN("No prior connection request for %s", address_with_type.ToString().c_str()); |
| } else { |
| connecting_le_.erase(connecting_addr_with_type); |
| } |
| if (create_connection_timeout_alarms_.find(address_with_type) != create_connection_timeout_alarms_.end()) { |
| create_connection_timeout_alarms_.at(address_with_type).Cancel(); |
| create_connection_timeout_alarms_.erase(address_with_type); |
| } |
| } |
| |
| void on_le_connection_complete(LeMetaEventView packet) { |
| LeConnectionCompleteView connection_complete = LeConnectionCompleteView::Create(packet); |
| ASSERT(connection_complete.IsValid()); |
| auto status = connection_complete.GetStatus(); |
| auto address = connection_complete.GetPeerAddress(); |
| auto peer_address_type = connection_complete.GetPeerAddressType(); |
| // TODO: find out which address and type was used to initiate the connection |
| AddressWithType remote_address(address, peer_address_type); |
| AddressWithType local_address = le_address_manager_->GetCurrentAddress(); |
| on_common_le_connection_complete(remote_address); |
| if (status == ErrorCode::UNKNOWN_CONNECTION && pause_connection) { |
| // connection canceled by LeAddressManager.OnPause(), will auto reconnect by LeAddressManager.OnResume() |
| return; |
| } else if (status == ErrorCode::UNKNOWN_CONNECTION && remote_address.GetAddress() == Address::kEmpty) { |
| // direct connect canceled due to connection timeout, start background connect |
| create_le_connection(remote_address, false, false); |
| return; |
| } else { |
| canceled_connections_.erase(remote_address); |
| ready_to_unregister = true; |
| remove_device_from_connect_list(remote_address); |
| } |
| |
| if (le_client_handler_ == nullptr) { |
| LOG_ERROR("No callbacks to call"); |
| return; |
| } |
| |
| if (status != ErrorCode::SUCCESS) { |
| le_client_handler_->Post(common::BindOnce(&LeConnectionCallbacks::OnLeConnectFail, |
| common::Unretained(le_client_callbacks_), remote_address, status)); |
| return; |
| } |
| |
| uint16_t conn_interval = connection_complete.GetConnInterval(); |
| uint16_t conn_latency = connection_complete.GetConnLatency(); |
| uint16_t supervision_timeout = connection_complete.GetSupervisionTimeout(); |
| if (!check_connection_parameters(conn_interval, conn_interval, conn_latency, supervision_timeout)) { |
| LOG_ERROR("Receive connection complete with invalid connection parameters"); |
| return; |
| } |
| |
| auto role = connection_complete.GetRole(); |
| uint16_t handle = connection_complete.GetConnectionHandle(); |
| auto queue = std::make_shared<AclConnection::Queue>(10); |
| auto emplace_pair = le_acl_connections_.emplace( |
| std::piecewise_construct, |
| std::forward_as_tuple(handle), |
| std::forward_as_tuple(remote_address, queue->GetDownEnd(), handler_)); |
| ASSERT(emplace_pair.second); // Make sure the connection is unique |
| auto& connection_proxy = emplace_pair.first->second; |
| round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::LE, handle, queue); |
| std::unique_ptr<LeAclConnection> connection(new LeAclConnection( |
| std::move(queue), le_acl_connection_interface_, handle, local_address, remote_address, role)); |
| connection_proxy.le_connection_management_callbacks_ = connection->GetEventCallbacks(); |
| le_client_handler_->Post(common::BindOnce(&LeConnectionCallbacks::OnLeConnectSuccess, |
| common::Unretained(le_client_callbacks_), remote_address, |
| std::move(connection))); |
| } |
| |
| void on_le_enhanced_connection_complete(LeMetaEventView packet) { |
| LeEnhancedConnectionCompleteView connection_complete = LeEnhancedConnectionCompleteView::Create(packet); |
| ASSERT(connection_complete.IsValid()); |
| auto status = connection_complete.GetStatus(); |
| auto address = connection_complete.GetPeerAddress(); |
| auto peer_address_type = connection_complete.GetPeerAddressType(); |
| auto peer_resolvable_address = connection_complete.GetPeerResolvablePrivateAddress(); |
| AddressWithType remote_address(address, peer_address_type); |
| if (!peer_resolvable_address.IsEmpty()) { |
| remote_address = AddressWithType(peer_resolvable_address, AddressType::RANDOM_DEVICE_ADDRESS); |
| } |
| on_common_le_connection_complete(remote_address); |
| if (status == ErrorCode::UNKNOWN_CONNECTION && pause_connection) { |
| // connection canceled by LeAddressManager.OnPause(), will auto reconnect by LeAddressManager.OnResume() |
| return; |
| } else if (status == ErrorCode::UNKNOWN_CONNECTION && remote_address.GetAddress() == Address::kEmpty) { |
| // direct connect canceled due to connection timeout, start background connect |
| create_le_connection(remote_address, false, false); |
| return; |
| } else { |
| canceled_connections_.erase(remote_address); |
| ready_to_unregister = true; |
| remove_device_from_connect_list(remote_address); |
| } |
| |
| if (le_client_handler_ == nullptr) { |
| LOG_ERROR("No callbacks to call"); |
| return; |
| } |
| |
| if (status != ErrorCode::SUCCESS) { |
| le_client_handler_->Post(common::BindOnce(&LeConnectionCallbacks::OnLeConnectFail, |
| common::Unretained(le_client_callbacks_), remote_address, status)); |
| return; |
| } |
| |
| auto role = connection_complete.GetRole(); |
| AddressWithType local_address; |
| if (role == hci::Role::CENTRAL) { |
| local_address = le_address_manager_->GetCurrentAddress(); |
| } else { |
| // when accepting connection, we must obtain the address from the advertiser. |
| // When we receive "set terminated event", we associate connection handle with advertiser address |
| local_address = AddressWithType{}; |
| } |
| |
| uint16_t conn_interval = connection_complete.GetConnInterval(); |
| uint16_t conn_latency = connection_complete.GetConnLatency(); |
| uint16_t supervision_timeout = connection_complete.GetSupervisionTimeout(); |
| if (!check_connection_parameters(conn_interval, conn_interval, conn_latency, supervision_timeout)) { |
| LOG_ERROR("Receive enhenced connection complete with invalid connection parameters"); |
| return; |
| } |
| uint16_t handle = connection_complete.GetConnectionHandle(); |
| auto queue = std::make_shared<AclConnection::Queue>(10); |
| auto emplace_pair = le_acl_connections_.emplace( |
| std::piecewise_construct, |
| std::forward_as_tuple(handle), |
| std::forward_as_tuple(remote_address, queue->GetDownEnd(), handler_)); |
| ASSERT(emplace_pair.second); // Make sure it's not a duplicate |
| auto& connection_proxy = emplace_pair.first->second; |
| round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::LE, handle, queue); |
| std::unique_ptr<LeAclConnection> connection(new LeAclConnection( |
| std::move(queue), le_acl_connection_interface_, handle, local_address, remote_address, role)); |
| connection_proxy.le_connection_management_callbacks_ = connection->GetEventCallbacks(); |
| le_client_handler_->Post(common::BindOnce(&LeConnectionCallbacks::OnLeConnectSuccess, |
| common::Unretained(le_client_callbacks_), remote_address, |
| std::move(connection))); |
| } |
| |
| 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; |
| } |
| auto handle = complete_view.GetConnectionHandle(); |
| auto callbacks = get_callbacks(handle); |
| if (callbacks == nullptr) { |
| LOG_WARN("Can't find connection 0x%hx", handle); |
| ASSERT(!crash_on_unknown_handle_); |
| return; |
| } |
| callbacks->OnConnectionUpdate( |
| complete_view.GetStatus(), |
| complete_view.GetConnInterval(), |
| complete_view.GetConnLatency(), |
| complete_view.GetSupervisionTimeout()); |
| } |
| |
| void on_le_phy_update_complete(LeMetaEventView view) { |
| auto complete_view = LePhyUpdateCompleteView::Create(view); |
| if (!complete_view.IsValid()) { |
| LOG_ERROR("Received on_le_phy_update_complete with invalid packet"); |
| return; |
| } |
| auto handle = complete_view.GetConnectionHandle(); |
| auto callbacks = get_callbacks(handle); |
| if (callbacks == nullptr) { |
| LOG_WARN("Can't find connection 0x%hx", handle); |
| ASSERT(!crash_on_unknown_handle_); |
| return; |
| } |
| callbacks->OnPhyUpdate(complete_view.GetStatus(), complete_view.GetTxPhy(), complete_view.GetRxPhy()); |
| } |
| |
| void on_le_read_remote_version_information( |
| hci::ErrorCode hci_status, uint16_t handle, uint8_t version, uint16_t manufacturer_name, uint16_t sub_version) { |
| auto callbacks = get_callbacks(handle); |
| if (callbacks == nullptr) { |
| LOG_INFO("No le connection registered for 0x%hx", handle); |
| return; |
| } |
| callbacks->OnReadRemoteVersionInformationComplete(hci_status, version, manufacturer_name, sub_version); |
| } |
| |
| void enqueue_command(std::unique_ptr<CommandBuilder> command_packet) { |
| hci_layer_->EnqueueCommand( |
| std::move(command_packet), |
| handler_->BindOnce(&LeAddressManager::OnCommandComplete, common::Unretained(le_address_manager_))); |
| } |
| |
| void on_data_length_change(LeMetaEventView view) { |
| auto data_length_view = LeDataLengthChangeView::Create(view); |
| if (!data_length_view.IsValid()) { |
| LOG_ERROR("Invalid packet"); |
| return; |
| } |
| auto handle = data_length_view.GetConnectionHandle(); |
| auto callbacks = get_callbacks(handle); |
| if (callbacks == nullptr) { |
| LOG_WARN("Can't find connection 0x%hx", handle); |
| ASSERT(!crash_on_unknown_handle_); |
| return; |
| } |
| callbacks->OnDataLengthChange( |
| data_length_view.GetMaxTxOctets(), |
| data_length_view.GetMaxTxTime(), |
| data_length_view.GetMaxRxOctets(), |
| data_length_view.GetMaxRxTime()); |
| } |
| |
| void on_remote_connection_parameter_request(LeMetaEventView view) { |
| auto request_view = LeRemoteConnectionParameterRequestView::Create(view); |
| if (!request_view.IsValid()) { |
| LOG_ERROR("Invalid packet"); |
| return; |
| } |
| |
| auto handle = request_view.GetConnectionHandle(); |
| auto callbacks = get_callbacks(handle); |
| if (callbacks == nullptr) { |
| LOG_WARN("Can't find connection 0x%hx", handle); |
| ASSERT(!crash_on_unknown_handle_); |
| return; |
| } |
| // TODO: this is blindly accepting any parameters, just so we don't hang connection |
| // have proper parameter negotiation |
| le_acl_connection_interface_->EnqueueCommand( |
| LeRemoteConnectionParameterRequestReplyBuilder::Create( |
| handle, |
| request_view.GetIntervalMin(), |
| request_view.GetIntervalMax(), |
| request_view.GetLatency(), |
| request_view.GetTimeout(), |
| 0, |
| 0), |
| handler_->BindOnce([](CommandCompleteView status) {})); |
| } |
| |
| void add_device_to_connect_list(AddressWithType address_with_type) { |
| AddressType address_type = address_with_type.GetAddressType(); |
| if (!address_manager_registered) { |
| le_address_manager_->Register(this); |
| address_manager_registered = true; |
| } |
| pause_connection = true; |
| switch (address_type) { |
| case AddressType::PUBLIC_DEVICE_ADDRESS: |
| case AddressType::PUBLIC_IDENTITY_ADDRESS: { |
| le_address_manager_->AddDeviceToConnectList(ConnectListAddressType::PUBLIC, address_with_type.GetAddress()); |
| } break; |
| case AddressType::RANDOM_DEVICE_ADDRESS: |
| case AddressType::RANDOM_IDENTITY_ADDRESS: { |
| le_address_manager_->AddDeviceToConnectList(ConnectListAddressType::RANDOM, address_with_type.GetAddress()); |
| } |
| } |
| } |
| |
| void add_device_to_resolving_list( |
| AddressWithType address_with_type, |
| const std::array<uint8_t, 16>& peer_irk, |
| const std::array<uint8_t, 16>& local_irk) { |
| AddressType address_type = address_with_type.GetAddressType(); |
| if (!address_manager_registered) { |
| le_address_manager_->Register(this); |
| address_manager_registered = true; |
| } |
| pause_connection = true; |
| switch (address_type) { |
| case AddressType::PUBLIC_DEVICE_ADDRESS: |
| case AddressType::PUBLIC_IDENTITY_ADDRESS: { |
| le_address_manager_->AddDeviceToResolvingList( |
| PeerAddressType::PUBLIC_DEVICE_OR_IDENTITY_ADDRESS, address_with_type.GetAddress(), peer_irk, local_irk); |
| } break; |
| case AddressType::RANDOM_DEVICE_ADDRESS: |
| case AddressType::RANDOM_IDENTITY_ADDRESS: { |
| le_address_manager_->AddDeviceToResolvingList( |
| PeerAddressType::RANDOM_DEVICE_OR_IDENTITY_ADDRESS, address_with_type.GetAddress(), peer_irk, local_irk); |
| } |
| } |
| } |
| |
| void create_le_connection(AddressWithType address_with_type, bool add_to_connect_list, bool is_direct) { |
| // TODO: Configure default LE connection parameters? |
| if (add_to_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()) { |
| create_connection_timeout_alarms_.emplace( |
| std::piecewise_construct, |
| std::forward_as_tuple(address_with_type.GetAddress(), address_with_type.GetAddressType()), |
| std::forward_as_tuple(handler_)); |
| create_connection_timeout_alarms_.at(address_with_type) |
| .Schedule( |
| common::BindOnce(&le_impl::on_create_connection_timeout, common::Unretained(this), address_with_type), |
| kCreateConnectionTimeoutMs); |
| } |
| } |
| } |
| |
| if (!address_manager_registered) { |
| auto policy = le_address_manager_->Register(this); |
| address_manager_registered = true; |
| |
| // Pause connection, wait for set random address complete |
| if (policy == LeAddressManager::AddressPolicy::USE_RESOLVABLE_ADDRESS || |
| policy == LeAddressManager::AddressPolicy::USE_NON_RESOLVABLE_ADDRESS) { |
| pause_connection = true; |
| } |
| } |
| |
| if (pause_connection) { |
| canceled_connections_.insert(address_with_type); |
| return; |
| } |
| if (le_client_callbacks_ == nullptr) { |
| LOG_ERROR("No callbacks to call"); |
| return; |
| } |
| |
| uint16_t le_scan_interval = kScanIntervalSlow; |
| uint16_t le_scan_window = kScanWindowSlow; |
| uint16_t le_scan_window_2m = kScanWindowSlow; |
| uint16_t le_scan_window_coded = kScanWindowSlow; |
| // If there is any direct connection in the connection list, use the fast parameter |
| if (!direct_connections_.empty()) { |
| le_scan_interval = kScanIntervalFast; |
| le_scan_window = kScanWindowFast; |
| le_scan_window_2m = kScanWindow2mFast; |
| le_scan_window_coded = kScanWindowCodedFast; |
| } |
| InitiatorFilterPolicy initiator_filter_policy = InitiatorFilterPolicy::USE_CONNECT_LIST; |
| OwnAddressType own_address_type = |
| static_cast<OwnAddressType>(le_address_manager_->GetCurrentAddress().GetAddressType()); |
| uint16_t conn_interval_min = 0x0018; |
| uint16_t conn_interval_max = 0x0028; |
| uint16_t conn_latency = 0x0000; |
| uint16_t supervision_timeout = 0x001f4; |
| ASSERT(check_connection_parameters(conn_interval_min, conn_interval_max, conn_latency, supervision_timeout)); |
| |
| connecting_le_.insert(address_with_type); |
| |
| if (initiator_filter_policy == InitiatorFilterPolicy::USE_CONNECT_LIST) { |
| address_with_type = AddressWithType(); |
| } |
| |
| if (controller_->IsSupported(OpCode::LE_EXTENDED_CREATE_CONNECTION)) { |
| uint8_t initiating_phys = PHY_LE_1M; |
| std::vector<LeCreateConnPhyScanParameters> parameters = {}; |
| LeCreateConnPhyScanParameters scan_parameters; |
| scan_parameters.scan_interval_ = le_scan_interval; |
| scan_parameters.scan_window_ = le_scan_window; |
| scan_parameters.conn_interval_min_ = conn_interval_min; |
| scan_parameters.conn_interval_max_ = conn_interval_max; |
| scan_parameters.conn_latency_ = conn_latency; |
| scan_parameters.supervision_timeout_ = supervision_timeout; |
| scan_parameters.min_ce_length_ = 0x00; |
| scan_parameters.max_ce_length_ = 0x00; |
| parameters.push_back(scan_parameters); |
| |
| if (controller_->SupportsBle2mPhy()) { |
| LeCreateConnPhyScanParameters scan_parameters_2m; |
| scan_parameters_2m.scan_interval_ = le_scan_interval; |
| scan_parameters_2m.scan_window_ = le_scan_window_2m; |
| scan_parameters_2m.conn_interval_min_ = conn_interval_min; |
| scan_parameters_2m.conn_interval_max_ = conn_interval_max; |
| scan_parameters_2m.conn_latency_ = conn_latency; |
| scan_parameters_2m.supervision_timeout_ = supervision_timeout; |
| scan_parameters_2m.min_ce_length_ = 0x00; |
| scan_parameters_2m.max_ce_length_ = 0x00; |
| parameters.push_back(scan_parameters_2m); |
| initiating_phys |= PHY_LE_2M; |
| } |
| if (controller_->SupportsBleCodedPhy()) { |
| LeCreateConnPhyScanParameters scan_parameters_coded; |
| scan_parameters_coded.scan_interval_ = le_scan_interval; |
| scan_parameters_coded.scan_window_ = le_scan_window_coded; |
| scan_parameters_coded.conn_interval_min_ = conn_interval_min; |
| scan_parameters_coded.conn_interval_max_ = conn_interval_max; |
| scan_parameters_coded.conn_latency_ = conn_latency; |
| scan_parameters_coded.supervision_timeout_ = supervision_timeout; |
| scan_parameters_coded.min_ce_length_ = 0x00; |
| scan_parameters_coded.max_ce_length_ = 0x00; |
| parameters.push_back(scan_parameters_coded); |
| initiating_phys |= PHY_LE_CODED; |
| } |
| |
| le_acl_connection_interface_->EnqueueCommand( |
| LeExtendedCreateConnectionBuilder::Create( |
| initiator_filter_policy, |
| own_address_type, |
| address_with_type.GetAddressType(), |
| address_with_type.GetAddress(), |
| initiating_phys, |
| parameters), |
| handler_->BindOnce([](CommandStatusView status) { |
| ASSERT(status.IsValid()); |
| ASSERT(status.GetCommandOpCode() == OpCode::LE_EXTENDED_CREATE_CONNECTION); |
| })); |
| } else { |
| le_acl_connection_interface_->EnqueueCommand( |
| 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, kMinimumCeLength, kMaximumCeLength), |
| handler_->BindOnce([](CommandStatusView status) { |
| ASSERT(status.IsValid()); |
| ASSERT(status.GetCommandOpCode() == OpCode::LE_CREATE_CONNECTION); |
| })); |
| } |
| } |
| |
| void on_create_connection_timeout(AddressWithType address_with_type) { |
| LOG_INFO("on_create_connection_timeout, address: %s", address_with_type.ToString().c_str()); |
| if (create_connection_timeout_alarms_.find(address_with_type) != create_connection_timeout_alarms_.end()) { |
| create_connection_timeout_alarms_.at(address_with_type).Cancel(); |
| create_connection_timeout_alarms_.erase(address_with_type); |
| if (background_connections_.find(address_with_type) != background_connections_.end()) { |
| direct_connections_.erase(address_with_type); |
| le_acl_connection_interface_->EnqueueCommand( |
| LeCreateConnectionCancelBuilder::Create(), |
| handler_->BindOnce(&le_impl::on_create_connection_cancel_complete, common::Unretained(this))); |
| } else { |
| cancel_connect(address_with_type); |
| } |
| le_client_handler_->Post(common::BindOnce( |
| &LeConnectionCallbacks::OnLeConnectFail, |
| common::Unretained(le_client_callbacks_), |
| address_with_type, |
| ErrorCode::CONNECTION_ACCEPT_TIMEOUT)); |
| } |
| } |
| |
| void cancel_connect(AddressWithType address_with_type) { |
| // the connection will be canceled by LeAddressManager.OnPause() |
| remove_device_from_connect_list(address_with_type); |
| } |
| |
| void set_le_suggested_default_data_parameters(uint16_t length, uint16_t time) { |
| auto packet = LeWriteSuggestedDefaultDataLengthBuilder::Create(length, time); |
| le_acl_connection_interface_->EnqueueCommand( |
| std::move(packet), handler_->BindOnce([](CommandCompleteView complete) {})); |
| } |
| |
| void remove_device_from_connect_list(AddressWithType address_with_type) { |
| AddressType address_type = address_with_type.GetAddressType(); |
| direct_connections_.erase(address_with_type); |
| if (!address_manager_registered) { |
| le_address_manager_->Register(this); |
| address_manager_registered = true; |
| } |
| pause_connection = true; |
| switch (address_type) { |
| case AddressType::PUBLIC_DEVICE_ADDRESS: |
| case AddressType::PUBLIC_IDENTITY_ADDRESS: { |
| le_address_manager_->RemoveDeviceFromConnectList( |
| ConnectListAddressType::PUBLIC, address_with_type.GetAddress()); |
| } break; |
| case AddressType::RANDOM_DEVICE_ADDRESS: |
| case AddressType::RANDOM_IDENTITY_ADDRESS: { |
| le_address_manager_->RemoveDeviceFromConnectList( |
| ConnectListAddressType::RANDOM, address_with_type.GetAddress()); |
| } |
| } |
| } |
| |
| void remove_device_from_resolving_list(AddressWithType address_with_type) { |
| AddressType address_type = address_with_type.GetAddressType(); |
| if (!address_manager_registered) { |
| le_address_manager_->Register(this); |
| address_manager_registered = true; |
| } |
| pause_connection = true; |
| switch (address_type) { |
| case AddressType::PUBLIC_DEVICE_ADDRESS: |
| case AddressType::PUBLIC_IDENTITY_ADDRESS: { |
| le_address_manager_->RemoveDeviceFromResolvingList( |
| PeerAddressType::PUBLIC_DEVICE_OR_IDENTITY_ADDRESS, address_with_type.GetAddress()); |
| } break; |
| case AddressType::RANDOM_DEVICE_ADDRESS: |
| case AddressType::RANDOM_IDENTITY_ADDRESS: { |
| le_address_manager_->RemoveDeviceFromResolvingList( |
| PeerAddressType::RANDOM_DEVICE_OR_IDENTITY_ADDRESS, address_with_type.GetAddress()); |
| } |
| } |
| } |
| |
| void clear_resolving_list() { |
| le_address_manager_->ClearResolvingList(); |
| } |
| |
| void set_privacy_policy_for_initiator_address( |
| LeAddressManager::AddressPolicy address_policy, |
| AddressWithType fixed_address, |
| crypto_toolbox::Octet16 rotation_irk, |
| std::chrono::milliseconds minimum_rotation_time, |
| std::chrono::milliseconds maximum_rotation_time) { |
| le_address_manager_->SetPrivacyPolicyForInitiatorAddress( |
| address_policy, |
| fixed_address, |
| rotation_irk, |
| controller_->SupportsBlePrivacy(), |
| minimum_rotation_time, |
| maximum_rotation_time); |
| } |
| |
| // TODO(jpawlowski): remove once we have config file abstraction in cert tests |
| void set_privacy_policy_for_initiator_address_for_test( |
| LeAddressManager::AddressPolicy address_policy, |
| AddressWithType fixed_address, |
| crypto_toolbox::Octet16 rotation_irk, |
| std::chrono::milliseconds minimum_rotation_time, |
| std::chrono::milliseconds maximum_rotation_time) { |
| le_address_manager_->SetPrivacyPolicyForInitiatorAddressForTest( |
| address_policy, fixed_address, rotation_irk, minimum_rotation_time, maximum_rotation_time); |
| } |
| |
| void handle_register_le_callbacks(LeConnectionCallbacks* callbacks, os::Handler* handler) { |
| ASSERT(le_client_callbacks_ == nullptr); |
| ASSERT(le_client_handler_ == nullptr); |
| le_client_callbacks_ = callbacks; |
| le_client_handler_ = handler; |
| } |
| |
| void handle_unregister_le_callbacks(LeConnectionCallbacks* callbacks, std::promise<void> promise) { |
| ASSERT_LOG(le_client_callbacks_ == callbacks, "Registered le callback entity is different then unregister request"); |
| le_client_callbacks_ = nullptr; |
| le_client_handler_ = nullptr; |
| promise.set_value(); |
| } |
| |
| bool 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; |
| } |
| |
| void add_device_to_background_connection_list(AddressWithType address_with_type) { |
| background_connections_.insert(address_with_type); |
| } |
| |
| void remove_device_from_background_connection_list(AddressWithType address_with_type) { |
| background_connections_.erase(address_with_type); |
| } |
| |
| void OnPause() override { |
| pause_connection = true; |
| if (connecting_le_.empty()) { |
| le_address_manager_->AckPause(this); |
| return; |
| } |
| canceled_connections_ = connecting_le_; |
| le_acl_connection_interface_->EnqueueCommand( |
| LeCreateConnectionCancelBuilder::Create(), |
| handler_->BindOnce(&le_impl::on_create_connection_cancel_complete, common::Unretained(this))); |
| le_address_manager_->AckPause(this); |
| } |
| |
| void on_create_connection_cancel_complete(CommandCompleteView view) { |
| auto complete_view = LeCreateConnectionCancelCompleteView::Create(view); |
| ASSERT(complete_view.IsValid()); |
| if (complete_view.GetStatus() != ErrorCode::SUCCESS) { |
| auto status = complete_view.GetStatus(); |
| std::string error_code = ErrorCodeText(status); |
| LOG_WARN("Received on_create_connection_cancel_complete with error code %s", error_code.c_str()); |
| } |
| } |
| |
| void check_for_unregister() { |
| if (le_acl_connections_.empty() && connecting_le_.empty() && canceled_connections_.empty() && |
| address_manager_registered && ready_to_unregister) { |
| le_address_manager_->Unregister(this); |
| address_manager_registered = false; |
| pause_connection = false; |
| ready_to_unregister = false; |
| } |
| } |
| |
| void OnResume() override { |
| pause_connection = false; |
| if (!canceled_connections_.empty()) { |
| create_le_connection(*canceled_connections_.begin(), false, false); |
| } |
| canceled_connections_.clear(); |
| le_address_manager_->AckResume(this); |
| check_for_unregister(); |
| } |
| |
| uint16_t HACK_get_handle(Address address) { |
| for (auto it = le_acl_connections_.begin(); it != le_acl_connections_.end(); it++) { |
| if (it->second.remote_address_.GetAddress() == address) { |
| return it->first; |
| } |
| } |
| return 0xFFFF; |
| } |
| |
| void UpdateLocalAddress(uint16_t handle, hci::AddressWithType address_with_type) { |
| auto callbacks = get_callbacks(handle); |
| if (callbacks == nullptr) { |
| LOG_WARN("Can't find connection 0x%hx", handle); |
| ASSERT(!crash_on_unknown_handle_); |
| return; |
| } |
| callbacks->OnLocalAddressUpdate(address_with_type); |
| } |
| |
| static constexpr uint16_t kMinimumCeLength = 0x0002; |
| static constexpr uint16_t kMaximumCeLength = 0x0C00; |
| HciLayer* hci_layer_ = nullptr; |
| Controller* controller_ = nullptr; |
| os::Handler* handler_ = nullptr; |
| RoundRobinScheduler* round_robin_scheduler_ = nullptr; |
| LeAddressManager* le_address_manager_ = nullptr; |
| LeAclConnectionInterface* le_acl_connection_interface_ = nullptr; |
| LeConnectionCallbacks* le_client_callbacks_ = nullptr; |
| os::Handler* le_client_handler_ = nullptr; |
| std::map<uint16_t, le_acl_connection> le_acl_connections_; |
| std::set<AddressWithType> connecting_le_; |
| std::set<AddressWithType> canceled_connections_; |
| std::set<AddressWithType> direct_connections_; |
| // Set of devices that will not be removed from connect list after direct connect timeout |
| std::set<AddressWithType> background_connections_; |
| bool address_manager_registered = false; |
| bool ready_to_unregister = false; |
| bool pause_connection = false; |
| bool crash_on_unknown_handle_ = false; |
| std::map<AddressWithType, os::Alarm> create_connection_timeout_alarms_; |
| }; |
| |
| } // namespace acl_manager |
| } // namespace hci |
| } // namespace bluetooth |