| /* |
| * 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 <base/bind.h> |
| #include <base/location.h> |
| #include <base/strings/stringprintf.h> |
| #include <cstdint> |
| #include <memory> |
| |
| #include "device/include/interop.h" |
| #include "hci/controller.h" |
| #include "main/shim/controller.h" |
| #include "main/shim/dumpsys.h" |
| #include "main/shim/link_policy.h" |
| #include "main/shim/stack.h" |
| #include "osi/include/log.h" |
| #include "stack/btm/btm_int_types.h" |
| #include "stack/include/btm_api.h" |
| #include "stack/include/btm_api_types.h" |
| #include "stack/include/btm_ble_api_types.h" |
| #include "stack/include/hci_error_code.h" |
| #include "stack/include/hcidefs.h" |
| |
| bt_status_t do_in_main_thread(const base::Location& from_here, |
| base::OnceClosure task); |
| |
| void btm_cont_rswitch_from_handle(uint16_t hci_handle); |
| void btm_pm_proc_mode_change(tHCI_STATUS hci_status, uint16_t hci_handle, |
| tHCI_MODE mode, uint16_t interval); |
| void btm_sco_chk_pend_unpark(tHCI_STATUS hci_status, uint16_t hci_handle); |
| void l2c_OnHciModeChangeSendPendingPackets(RawAddress remote); |
| void process_ssr_event(tHCI_STATUS status, uint16_t handle, |
| UNUSED_ATTR uint16_t max_tx_lat, uint16_t max_rx_lat); |
| tACL_CONN* acl_get_connection_from_handle(uint16_t handle); |
| |
| extern tBTM_CB btm_cb; |
| |
| namespace { |
| |
| tBTM_STATUS set_active_mode(tACL_CONN& p_acl) { |
| bluetooth::shim::Stack::GetInstance()->LinkPolicy()->ExitSniffMode( |
| p_acl.hci_handle); |
| return BTM_SUCCESS; |
| } |
| |
| tBTM_STATUS set_hold_mode(tACL_CONN& p_acl, uint16_t max, uint16_t min) { |
| bluetooth::shim::Stack::GetInstance()->LinkPolicy()->HoldMode( |
| p_acl.hci_handle, max, min); |
| return BTM_SUCCESS; |
| } |
| |
| tBTM_STATUS set_sniff_mode(tACL_CONN& p_acl, uint16_t max_interval, |
| uint16_t min_interval, uint16_t attempt, |
| uint16_t timeout) { |
| bluetooth::shim::Stack::GetInstance()->LinkPolicy()->SniffMode( |
| p_acl.hci_handle, max_interval, min_interval, attempt, timeout); |
| return BTM_SUCCESS; |
| } |
| |
| bool controller_supports_link_policy_mode(const tBTM_PM_MODE& mode, |
| bool interop_check) { |
| switch (mode) { |
| case BTM_PM_MD_ACTIVE: // Active mode is always supported |
| break; |
| case BTM_PM_MD_PARK: // Park mode no longer supported |
| return false; |
| case BTM_PM_MD_SNIFF: |
| if (!controller_get_interface()->supports_sniff_mode() || interop_check) |
| return false; |
| break; |
| case BTM_PM_MD_HOLD: |
| if (!controller_get_interface()->supports_hold_mode() || interop_check) |
| return false; |
| break; |
| default: |
| LOG_ERROR("Unknown mode:%u", mode); |
| return false; |
| } |
| return true; |
| } |
| |
| } // namespace |
| |
| bool bluetooth::shim::RegisterLinkPolicyClient(tBTM_PM_STATUS_CBACK* p_cb) { |
| if (std::find(btm_cb.acl_cb_.link_policy.clients.begin(), |
| btm_cb.acl_cb_.link_policy.clients.end(), |
| p_cb) != btm_cb.acl_cb_.link_policy.clients.end()) { |
| LOG_ERROR("Link policy client already registered"); |
| return false; |
| } |
| btm_cb.acl_cb_.link_policy.clients.push_back(p_cb); |
| return true; |
| } |
| |
| bool bluetooth::shim::UnregisterLinkPolicyClient(tBTM_PM_STATUS_CBACK* p_cb) { |
| auto cb = std::find(btm_cb.acl_cb_.link_policy.clients.begin(), |
| btm_cb.acl_cb_.link_policy.clients.end(), p_cb); |
| if (cb == btm_cb.acl_cb_.link_policy.clients.end()) { |
| LOG_ERROR("Link policy client already unregistered"); |
| return false; |
| } |
| btm_cb.acl_cb_.link_policy.clients.erase(cb); |
| return true; |
| } |
| |
| tBTM_STATUS bluetooth::shim::BTM_SetPowerMode(uint16_t handle, |
| const tBTM_PM_PWR_MD& new_mode) { |
| tACL_CONN* p_acl = acl_get_connection_from_handle(handle); |
| if (p_acl == nullptr) { |
| return BTM_UNKNOWN_ADDR; |
| } |
| |
| if (!controller_supports_link_policy_mode( |
| new_mode.mode, |
| interop_match_addr(INTEROP_DISABLE_SNIFF, &p_acl->remote_addr))) { |
| return BTM_MODE_UNSUPPORTED; |
| } |
| |
| if (p_acl->policy.Mode() == new_mode.mode) { |
| LOG_INFO("Controller already in mode:%s[0x%02x]", |
| power_mode_state_text(p_acl->policy.Mode()).c_str(), |
| p_acl->policy.Mode()); |
| } |
| |
| if (p_acl->policy.mode.IsPending()) { |
| LOG_INFO("Link policy mode is pending"); |
| } |
| |
| LOG_INFO("Switching mode from %s(0x%x) to %s(0x%x)", |
| power_mode_state_text(p_acl->policy.Mode()).c_str(), |
| p_acl->policy.Mode(), power_mode_state_text(new_mode.mode).c_str(), |
| new_mode.mode); |
| |
| p_acl->policy.mode.pending_ = new_mode.mode; |
| switch (new_mode.mode) { |
| case BTM_PM_MD_ACTIVE: |
| set_active_mode(*p_acl); |
| return BTM_SUCCESS; |
| break; |
| case BTM_PM_MD_SNIFF: |
| set_sniff_mode(*p_acl, new_mode.max, new_mode.min, new_mode.attempt, |
| new_mode.timeout); |
| return BTM_SUCCESS; |
| break; |
| case BTM_PM_MD_HOLD: |
| return set_hold_mode(*p_acl, new_mode.max, new_mode.min); |
| break; |
| } |
| return BTM_MODE_UNSUPPORTED; |
| } |
| |
| static bool is_encryption_pause_supported(const tACL_CONN& p_acl) { |
| CHECK(p_acl.peer_lmp_feature_valid[0]) |
| << "Checked before remote feature read has complete"; |
| return HCI_ATOMIC_ENCRYPT_SUPPORTED(p_acl.peer_lmp_feature_pages[0]) && |
| controller_get_interface()->supports_encryption_pause(); |
| } |
| |
| void bluetooth::shim::btm_pm_on_mode_change(tHCI_STATUS status, uint16_t handle, |
| tHCI_MODE hci_mode, |
| uint16_t interval) { |
| tBTM_PM_MODE new_mode = HCI_TO_BTM_POWER_MODE(hci_mode); |
| |
| LOG_DEBUG( |
| "For now pointing back again to legacy status:%s handle:0x%04x " |
| "new_mode:%u interval:%u", |
| hci_error_code_text(status).c_str(), handle, new_mode, interval); |
| |
| tACL_CONN* p_acl = acl_get_connection_from_handle(handle); |
| if (p_acl == nullptr) { |
| LOG_ERROR("Received mode change for unknown acl handle:0x%04x", handle); |
| return; |
| } |
| |
| tBTM_PM_MODE pending = p_acl->policy.mode.Pending(); |
| p_acl->policy.mode.pending_ = BTM_PM_MD_UNKNOWN; |
| |
| if (status == HCI_SUCCESS) { |
| BTM_LogHistory( |
| "Power", p_acl->remote_addr, "Mode change", |
| base::StringPrintf("%s[0x%02x] ==> %s[0x%02x] pending:%s", |
| power_mode_state_text(p_acl->policy.Mode()).c_str(), |
| p_acl->policy.Mode(), |
| power_mode_state_text(new_mode).c_str(), new_mode, |
| power_mode_state_text(pending).c_str())); |
| LOG_INFO("Power mode switched from %s[%hhu] to %s[%hhu] pending:%s", |
| power_mode_state_text(p_acl->policy.Mode()).c_str(), |
| p_acl->policy.Mode(), power_mode_state_text(new_mode).c_str(), |
| new_mode, power_mode_state_text(pending).c_str()); |
| p_acl->policy.mode.mode_ = new_mode; |
| |
| if (new_mode == (BTM_PM_ST_ACTIVE) || new_mode == (BTM_PM_ST_SNIFF)) { |
| l2c_OnHciModeChangeSendPendingPackets(p_acl->remote_addr); |
| } |
| |
| /*check if sco disconnect is waiting for the mode change */ |
| btm_sco_disc_chk_pend_for_modechange(handle); |
| |
| if (p_acl->is_switch_role_mode_change()) { |
| if (p_acl->is_encrypted && !is_encryption_pause_supported(*p_acl)) { |
| p_acl->set_encryption_off(); |
| p_acl->set_switch_role_encryption_off(); |
| } else { |
| p_acl->set_switch_role_in_progress(); |
| p_acl->rs_disc_pending = BTM_SEC_RS_PENDING; |
| bluetooth::legacy::hci::GetInterface().StartRoleSwitch( |
| p_acl->remote_addr, HCI_ROLE_CENTRAL); |
| } |
| } |
| } |
| |
| btm_sco_chk_pend_unpark(status, handle); |
| // btm_pm_proc_mode_change(status, handle, new_mode, interval); |
| |
| for (auto client_callback : btm_cb.acl_cb_.link_policy.clients) { |
| (*client_callback)(p_acl->remote_addr, new_mode, interval, status); |
| } |
| |
| LOG_DEBUG( |
| "Notified mode change registered clients cnt:%zu peer:%s " |
| "status:%s", |
| btm_cb.acl_cb_.link_policy.clients.size(), |
| PRIVATE_ADDRESS(p_acl->remote_addr), hci_error_code_text(status).c_str()); |
| } |
| |
| tBTM_STATUS bluetooth::shim::BTM_SetSsrParams(uint16_t handle, uint16_t max_lat, |
| uint16_t min_rmt_to, |
| uint16_t min_loc_to) { |
| LOG_DEBUG("Sending gd power mode SSR Params"); |
| tACL_CONN* p_acl = acl_get_connection_from_handle(handle); |
| if (p_acl == nullptr) { |
| return BTM_UNKNOWN_ADDR; |
| } |
| |
| p_acl->policy.sniff_subrating.pending_ = true; |
| bluetooth::shim::Stack::GetInstance()->LinkPolicy()->SniffSubrating( |
| handle, max_lat, min_rmt_to, min_loc_to); |
| return BTM_SUCCESS; |
| } |
| |
| void bluetooth::shim::btm_pm_on_sniff_subrating( |
| tHCI_STATUS status, uint16_t handle, uint16_t maximum_transmit_latency, |
| UNUSED_ATTR uint16_t maximum_receive_latency, |
| uint16_t minimum_remote_timeout, uint16_t minimum_local_timeout) { |
| LOG_DEBUG("For now pointing back again to legacy"); |
| tACL_CONN* p_acl = acl_get_connection_from_handle(handle); |
| if (p_acl == nullptr) { |
| LOG_ERROR("Received mode change for unknown acl handle:0x%04x", handle); |
| return; |
| } |
| |
| p_acl->policy.sniff_subrating.pending_ = false; |
| if (status == HCI_SUCCESS) { |
| BTM_LogHistory( |
| "Power", p_acl->remote_addr, "Sniff Subrating", |
| base::StringPrintf( |
| "max_xmit_latency:%.2fs remote_timeout:%.2fs local_timeout:%.2fs", |
| ticks_to_seconds(maximum_transmit_latency), |
| ticks_to_seconds(minimum_remote_timeout), |
| ticks_to_seconds(minimum_local_timeout))); |
| } |
| |
| const bool use_ssr = |
| (p_acl->policy.mode.Interval() != maximum_receive_latency) ? true : false; |
| |
| for (auto client_callback : btm_cb.acl_cb_.link_policy.clients) { |
| (*client_callback)(p_acl->remote_addr, BTM_PM_STS_SSR, (use_ssr) ? 1 : 0, |
| status); |
| } |
| |
| LOG_DEBUG( |
| "Notified sniff subrating registered clients cnt:%zu peer:%s use_ssr:%s " |
| "status:%s", |
| btm_cb.acl_cb_.link_policy.clients.size(), |
| PRIVATE_ADDRESS(p_acl->remote_addr), logbool(use_ssr).c_str(), |
| hci_error_code_text(status).c_str()); |
| } |