1 /* 2 * Copyright 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #pragma once 18 19 #define LOG_TAG "bt_headless_mode" 20 21 #include <bluetooth/log.h> 22 23 #include <future> 24 #include <mutex> 25 26 #include "bta/dm/bta_dm_int.h" 27 #include "stack/include/btm_client_interface.h" 28 #include "stack/include/btm_status.h" 29 #include "stack/include/hci_error_code.h" 30 #include "types/raw_address.h" 31 32 using namespace std::chrono_literals; 33 using namespace bluetooth; 34 35 namespace { 36 const tBTM_PM_PWR_MD default_mandatory_sniff_mode = { 37 .max = 0x0006, 38 .min = 0x0006, 39 .attempt = 0x0020, 40 .timeout = 0x7fff, 41 .mode = BTM_PM_MD_SNIFF, 42 }; 43 44 const tBTM_PM_PWR_MD typical_sniff_mode = { 45 .max = 800, // 5 seconds 46 .min = 400, // 2.5 seconds 47 .attempt = 4, 48 .timeout = 1, 49 .mode = BTM_PM_MD_SNIFF, 50 }; 51 52 const tBTM_PM_PWR_MD default_active_mode = { 53 .max = 0, // Unused 54 .min = 0, // Unused 55 .attempt = 0, // Unused 56 .timeout = 0, // Unused 57 .mode = BTM_PM_MD_ACTIVE, 58 }; 59 } // namespace 60 61 // tBTM_PM_STATUS_CBACK 62 struct power_mode_callback_t { 63 const RawAddress bd_addr; 64 tBTM_PM_STATUS status; 65 uint16_t value; 66 tHCI_STATUS hci_status; 67 ToStringpower_mode_callback_t68 std::string ToString() const { 69 return fmt::format("bd_addr:{} pm_status:{} value:{} hci_status:{}", 70 bd_addr.ToString(), power_mode_status_text(status), 71 value, hci_status_code_text(hci_status)); 72 } 73 }; 74 75 struct pwr_command_t { 76 std::promise<power_mode_callback_t> cmd_status_promise; 77 std::promise<power_mode_callback_t> mode_event_promise; 78 }; 79 80 struct pwr_result_t { 81 tBTM_STATUS btm_status; 82 std::future<power_mode_callback_t> cmd_status_future; 83 std::future<power_mode_callback_t> mode_event_future; 84 }; 85 86 namespace { 87 88 class Queue { 89 public: CallbackReceived(const power_mode_callback_t & data)90 void CallbackReceived(const power_mode_callback_t& data) { 91 log::info("Power mode callback cnt:{} data:{}", cnt++, data.ToString()); 92 std::unique_lock<std::mutex> lk(mutex); 93 if (promises_map_[data.bd_addr].empty()) { 94 log::info("Received unsolicited power mode callback: {}", 95 data.ToString()); 96 return; 97 } 98 promises_map_[data.bd_addr].front().set_value(data); 99 promises_map_[data.bd_addr].pop_front(); 100 } 101 CommandSent(const RawAddress & bd_addr,pwr_command_t && pwr_command)102 void CommandSent(const RawAddress& bd_addr, pwr_command_t&& pwr_command) { 103 std::unique_lock<std::mutex> lk(mutex); 104 promises_map_[bd_addr].push_back(std::move(pwr_command.cmd_status_promise)); 105 promises_map_[bd_addr].push_back(std::move(pwr_command.mode_event_promise)); 106 } 107 PopFront(const RawAddress & bd_addr)108 void PopFront(const RawAddress& bd_addr) { 109 std::unique_lock<std::mutex> lk(mutex); 110 log::assert_that(!promises_map_[bd_addr].empty(), 111 "Unable to remove promise from empty bag of promises"); 112 promises_map_[bd_addr].pop_front(); 113 } 114 115 private: 116 mutable std::mutex mutex; 117 std::unordered_map<RawAddress, 118 std::deque<std::promise<power_mode_callback_t>>> 119 promises_map_; 120 size_t cnt = 0; 121 122 } queue_; 123 124 } // namespace 125 126 class PowerMode { 127 public: 128 class Client { 129 public: Client(const uint8_t pm_id,const RawAddress & bd_addr)130 Client(const uint8_t pm_id, const RawAddress& bd_addr) 131 : pm_id_(pm_id), bd_addr_(bd_addr) {} 132 133 // Used when the power mode command status is unsuccessful 134 // to prevent waiting for a mode event that will never arrive. 135 // Exposed to allow testing of these conditions. remove_mode_event_promise()136 void remove_mode_event_promise() { queue_.PopFront(bd_addr_); } 137 set_sniff(pwr_command_t && pwr_command)138 pwr_result_t set_sniff(pwr_command_t&& pwr_command) { 139 return send_power_mode_command( 140 std::move(pwr_command), 141 get_btm_client_interface().link_policy.BTM_SetPowerMode( 142 pm_id_, bd_addr_, &default_mandatory_sniff_mode)); 143 } set_typical_sniff(pwr_command_t && pwr_command)144 pwr_result_t set_typical_sniff(pwr_command_t&& pwr_command) { 145 return send_power_mode_command( 146 std::move(pwr_command), 147 get_btm_client_interface().link_policy.BTM_SetPowerMode( 148 pm_id_, bd_addr_, &typical_sniff_mode)); 149 } 150 set_active(pwr_command_t && pwr_command)151 pwr_result_t set_active(pwr_command_t&& pwr_command) { 152 return send_power_mode_command( 153 std::move(pwr_command), 154 get_btm_client_interface().link_policy.BTM_SetPowerMode( 155 pm_id_, bd_addr_, &default_active_mode)); 156 } 157 158 private: send_power_mode_command(pwr_command_t && pwr_command,const tBTM_STATUS btm_status)159 pwr_result_t send_power_mode_command(pwr_command_t&& pwr_command, 160 const tBTM_STATUS btm_status) { 161 pwr_result_t result = { 162 .btm_status = btm_status, 163 .cmd_status_future = pwr_command.cmd_status_promise.get_future(), 164 .mode_event_future = pwr_command.mode_event_promise.get_future(), 165 }; 166 queue_.CommandSent(bd_addr_, std::move(pwr_command)); 167 return result; 168 } 169 170 const uint8_t pm_id_; 171 const RawAddress bd_addr_; 172 }; 173 PowerMode()174 PowerMode() { 175 BTM_PmRegister(BTM_PM_DEREG, &bta_dm_cb.pm_id, 176 []([[maybe_unused]] const RawAddress& bd_addr, 177 [[maybe_unused]] tBTM_PM_STATUS status, 178 [[maybe_unused]] uint16_t value, 179 [[maybe_unused]] tHCI_STATUS hci_status) {}); 180 181 tBTM_STATUS btm_status = 182 get_btm_client_interface().lifecycle.BTM_PmRegister( 183 BTM_PM_REG_SET, &pm_id_, 184 [](const RawAddress& bd_addr, tBTM_PM_STATUS status, uint16_t value, 185 tHCI_STATUS hci_status) { 186 queue_.CallbackReceived(power_mode_callback_t{ 187 .bd_addr = bd_addr, 188 .status = status, 189 .value = value, 190 .hci_status = hci_status, 191 }); 192 }); 193 194 log::assert_that(BTM_SUCCESS == btm_status, 195 "Failed to register power mode:{}", 196 btm_status_text(btm_status)); 197 } 198 ~PowerMode()199 ~PowerMode() { 200 auto status = get_btm_client_interface().lifecycle.BTM_PmRegister( 201 BTM_PM_DEREG, &pm_id_, 202 []([[maybe_unused]] const RawAddress& bd_addr, 203 [[maybe_unused]] tBTM_PM_STATUS status, 204 [[maybe_unused]] uint16_t value, 205 [[maybe_unused]] tHCI_STATUS hci_status) {}); 206 log::assert_that(BTM_SUCCESS == status, 207 "assert failed: BTM_SUCCESS == status"); 208 } 209 GetClient(const RawAddress bd_addr)210 Client GetClient(const RawAddress bd_addr) { return Client(pm_id_, bd_addr); } 211 212 private: 213 uint8_t pm_id_; 214 }; 215