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