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 std::format("bd_addr:{} pm_status:{} value:{} hci_status:{}", bd_addr.ToString(),
70                        power_mode_status_text(status), value, hci_status_code_text(hci_status));
71   }
72 };
73 
74 struct pwr_command_t {
75   std::promise<power_mode_callback_t> cmd_status_promise;
76   std::promise<power_mode_callback_t> mode_event_promise;
77 };
78 
79 struct pwr_result_t {
80   tBTM_STATUS btm_status;
81   std::future<power_mode_callback_t> cmd_status_future;
82   std::future<power_mode_callback_t> mode_event_future;
83 };
84 
85 namespace {
86 
87 class Queue {
88 public:
CallbackReceived(const power_mode_callback_t & data)89   void CallbackReceived(const power_mode_callback_t& data) {
90     log::info("Power mode callback cnt:{} data:{}", cnt++, data.ToString());
91     std::unique_lock<std::mutex> lk(mutex);
92     if (promises_map_[data.bd_addr].empty()) {
93       log::info("Received unsolicited power mode callback: {}", data.ToString());
94       return;
95     }
96     promises_map_[data.bd_addr].front().set_value(data);
97     promises_map_[data.bd_addr].pop_front();
98   }
99 
CommandSent(const RawAddress & bd_addr,pwr_command_t && pwr_command)100   void CommandSent(const RawAddress& bd_addr, pwr_command_t&& pwr_command) {
101     std::unique_lock<std::mutex> lk(mutex);
102     promises_map_[bd_addr].push_back(std::move(pwr_command.cmd_status_promise));
103     promises_map_[bd_addr].push_back(std::move(pwr_command.mode_event_promise));
104   }
105 
PopFront(const RawAddress & bd_addr)106   void PopFront(const RawAddress& bd_addr) {
107     std::unique_lock<std::mutex> lk(mutex);
108     log::assert_that(!promises_map_[bd_addr].empty(),
109                      "Unable to remove promise from empty bag of promises");
110     promises_map_[bd_addr].pop_front();
111   }
112 
113 private:
114   mutable std::mutex mutex;
115   std::unordered_map<RawAddress, std::deque<std::promise<power_mode_callback_t>>> promises_map_;
116   size_t cnt = 0;
117 } queue_;
118 
119 }  // namespace
120 
121 class PowerMode {
122 public:
123   class Client {
124   public:
Client(const uint8_t pm_id,const RawAddress & bd_addr)125     Client(const uint8_t pm_id, const RawAddress& bd_addr) : pm_id_(pm_id), bd_addr_(bd_addr) {}
126 
127     // Used when the power mode command status is unsuccessful
128     // to prevent waiting for a mode event that will never arrive.
129     // Exposed to allow testing of these conditions.
remove_mode_event_promise()130     void remove_mode_event_promise() { queue_.PopFront(bd_addr_); }
131 
set_sniff(pwr_command_t && pwr_command)132     pwr_result_t set_sniff(pwr_command_t&& pwr_command) {
133       return send_power_mode_command(std::move(pwr_command),
134                                      get_btm_client_interface().link_policy.BTM_SetPowerMode(
135                                              pm_id_, bd_addr_, &default_mandatory_sniff_mode));
136     }
set_typical_sniff(pwr_command_t && pwr_command)137     pwr_result_t set_typical_sniff(pwr_command_t&& pwr_command) {
138       return send_power_mode_command(std::move(pwr_command),
139                                      get_btm_client_interface().link_policy.BTM_SetPowerMode(
140                                              pm_id_, bd_addr_, &typical_sniff_mode));
141     }
142 
set_active(pwr_command_t && pwr_command)143     pwr_result_t set_active(pwr_command_t&& pwr_command) {
144       return send_power_mode_command(std::move(pwr_command),
145                                      get_btm_client_interface().link_policy.BTM_SetPowerMode(
146                                              pm_id_, bd_addr_, &default_active_mode));
147     }
148 
149   private:
send_power_mode_command(pwr_command_t && pwr_command,const tBTM_STATUS btm_status)150     pwr_result_t send_power_mode_command(pwr_command_t&& pwr_command,
151                                          const tBTM_STATUS btm_status) {
152       pwr_result_t result = {
153               .btm_status = btm_status,
154               .cmd_status_future = pwr_command.cmd_status_promise.get_future(),
155               .mode_event_future = pwr_command.mode_event_promise.get_future(),
156       };
157       queue_.CommandSent(bd_addr_, std::move(pwr_command));
158       return result;
159     }
160 
161     const uint8_t pm_id_;
162     const RawAddress bd_addr_;
163   };
164 
PowerMode()165   PowerMode() {
166     BTM_PmRegister(
167             BTM_PM_DEREG, &bta_dm_cb.pm_id,
168             []([[maybe_unused]] const RawAddress& bd_addr, [[maybe_unused]] tBTM_PM_STATUS status,
169                [[maybe_unused]] uint16_t value, [[maybe_unused]] tHCI_STATUS hci_status) {});
170 
171     tBTM_STATUS btm_status = get_btm_client_interface().lifecycle.BTM_PmRegister(
172             BTM_PM_REG_SET, &pm_id_,
173             [](const RawAddress& bd_addr, tBTM_PM_STATUS status, uint16_t value,
174                tHCI_STATUS hci_status) {
175               queue_.CallbackReceived(power_mode_callback_t{
176                       .bd_addr = bd_addr,
177                       .status = status,
178                       .value = value,
179                       .hci_status = hci_status,
180               });
181             });
182 
183     log::assert_that(tBTM_STATUS::BTM_SUCCESS == btm_status, "Failed to register power mode:{}",
184                      btm_status_text(btm_status));
185   }
186 
~PowerMode()187   ~PowerMode() {
188     auto status = get_btm_client_interface().lifecycle.BTM_PmRegister(
189             BTM_PM_DEREG, &pm_id_,
190             []([[maybe_unused]] const RawAddress& bd_addr, [[maybe_unused]] tBTM_PM_STATUS status,
191                [[maybe_unused]] uint16_t value, [[maybe_unused]] tHCI_STATUS hci_status) {});
192     log::assert_that(tBTM_STATUS::BTM_SUCCESS == status, "assert failed: BTM_SUCCESS == status");
193   }
194 
GetClient(const RawAddress bd_addr)195   Client GetClient(const RawAddress bd_addr) { return Client(pm_id_, bd_addr); }
196 
197 private:
198   uint8_t pm_id_;
199 };
200