1 /******************************************************************************
2 *
3 * Copyright 2018 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 ******************************************************************************/
18
19 #include "connection_manager.h"
20
21 #include <base/bind.h>
22 #include <base/callback.h>
23 #include <base/location.h>
24 #include <base/logging.h>
25 #include <map>
26 #include <memory>
27 #include <set>
28
29 #include "internal_include/bt_trace.h"
30 #include "main/shim/shim.h"
31 #include "osi/include/alarm.h"
32 #include "osi/include/log.h"
33 #include "stack/btm/btm_ble_bgconn.h"
34 #include "stack/include/l2c_api.h"
35
36 #define DIRECT_CONNECT_TIMEOUT (30 * 1000) /* 30 seconds */
37
38 struct closure_data {
39 base::OnceClosure user_task;
40 base::Location posted_from;
41 };
42
alarm_closure_cb(void * p)43 static void alarm_closure_cb(void* p) {
44 closure_data* data = (closure_data*)p;
45 VLOG(1) << "executing timer scheduled at %s" << data->posted_from.ToString();
46 std::move(data->user_task).Run();
47 delete data;
48 }
49
50 // Periodic alarms are not supported, because we clean up data in callback
alarm_set_closure(const base::Location & posted_from,alarm_t * alarm,uint64_t interval_ms,base::OnceClosure user_task)51 void alarm_set_closure(const base::Location& posted_from, alarm_t* alarm,
52 uint64_t interval_ms, base::OnceClosure user_task) {
53 closure_data* data = new closure_data;
54 data->posted_from = posted_from;
55 data->user_task = std::move(user_task);
56 VLOG(1) << "scheduling timer %s" << data->posted_from.ToString();
57 alarm_set_on_mloop(alarm, interval_ms, alarm_closure_cb, data);
58 }
59
60 using unique_alarm_ptr = std::unique_ptr<alarm_t, decltype(&alarm_free)>;
61
62 namespace connection_manager {
63
64 struct tAPPS_CONNECTING {
65 // ids of clients doing background connection to given device
66 std::set<tAPP_ID> doing_bg_conn;
67
68 // Apps trying to do direct connection.
69 std::map<tAPP_ID, unique_alarm_ptr> doing_direct_conn;
70 };
71
72 namespace {
73 // Maps address to apps trying to connect to it
74 std::map<RawAddress, tAPPS_CONNECTING> bgconn_dev;
75
anyone_connecting(const std::map<RawAddress,tAPPS_CONNECTING>::iterator it)76 bool anyone_connecting(
77 const std::map<RawAddress, tAPPS_CONNECTING>::iterator it) {
78 return (!it->second.doing_bg_conn.empty() ||
79 !it->second.doing_direct_conn.empty());
80 }
81
82 } // namespace
83
84 /** background connection device from the list. Returns pointer to the device
85 * record, or nullptr if not found */
get_apps_connecting_to(const RawAddress & address)86 std::set<tAPP_ID> get_apps_connecting_to(const RawAddress& address) {
87 auto it = bgconn_dev.find(address);
88 return (it != bgconn_dev.end()) ? it->second.doing_bg_conn
89 : std::set<tAPP_ID>();
90 }
91
92 /** Add a device from the background connection list. Returns true if device
93 * added to the list, or already in list, false otherwise */
background_connect_add(uint8_t app_id,const RawAddress & address)94 bool background_connect_add(uint8_t app_id, const RawAddress& address) {
95 if (bluetooth::shim::is_gd_l2cap_enabled()) {
96 return L2CA_ConnectFixedChnl(L2CAP_ATT_CID, address);
97 }
98
99 auto it = bgconn_dev.find(address);
100 bool in_acceptlist = false;
101 if (it != bgconn_dev.end()) {
102 // device already in the acceptlist, just add interested app to the list
103 if (it->second.doing_bg_conn.count(app_id)) {
104 LOG(INFO) << "App id=" << loghex(app_id)
105 << "already doing background connection to " << address;
106 return true;
107 }
108
109 // Already in acceptlist ?
110 if (anyone_connecting(it)) {
111 in_acceptlist = true;
112 }
113 }
114
115 if (!in_acceptlist) {
116 // the device is not in the acceptlist
117 if (!BTM_AcceptlistAdd(address)) return false;
118 }
119
120 // create endtry for address, and insert app_id.
121 bgconn_dev[address].doing_bg_conn.insert(app_id);
122 return true;
123 }
124
125 /** Removes all registrations for connection for given device.
126 * Returns true if anything was removed, false otherwise */
remove_unconditional(const RawAddress & address)127 bool remove_unconditional(const RawAddress& address) {
128 auto it = bgconn_dev.find(address);
129 if (it == bgconn_dev.end()) return false;
130
131 BTM_AcceptlistRemove(address);
132 bgconn_dev.erase(it);
133 return true;
134 }
135
136 /** Remove device from the background connection device list or listening to
137 * advertising list. Returns true if device was on the list and was succesfully
138 * removed */
background_connect_remove(uint8_t app_id,const RawAddress & address)139 bool background_connect_remove(uint8_t app_id, const RawAddress& address) {
140 VLOG(2) << __func__;
141 auto it = bgconn_dev.find(address);
142 if (it == bgconn_dev.end()) return false;
143
144 if (!it->second.doing_bg_conn.erase(app_id)) return false;
145
146 if (anyone_connecting(it)) return true;
147
148 // no more apps interested - remove from acceptlist and delete record
149 BTM_AcceptlistRemove(address);
150 bgconn_dev.erase(it);
151 return true;
152 }
153
154 /** deregister all related background connetion device. */
on_app_deregistered(uint8_t app_id)155 void on_app_deregistered(uint8_t app_id) {
156 auto it = bgconn_dev.begin();
157 auto end = bgconn_dev.end();
158 /* update the BG conn device list */
159 while (it != end) {
160 it->second.doing_bg_conn.erase(app_id);
161
162 it->second.doing_direct_conn.erase(app_id);
163
164 if (anyone_connecting(it)) {
165 it++;
166 continue;
167 }
168
169 BTM_AcceptlistRemove(it->first);
170 it = bgconn_dev.erase(it);
171 }
172 }
173
remove_all_clients_with_pending_connections(const RawAddress & address)174 static void remove_all_clients_with_pending_connections(
175 const RawAddress& address) {
176 auto it = bgconn_dev.find(address);
177 while (it != bgconn_dev.end() && !it->second.doing_direct_conn.empty()) {
178 uint8_t app_id = it->second.doing_direct_conn.begin()->first;
179 direct_connect_remove(app_id, address);
180 it = bgconn_dev.find(address);
181 }
182 }
183
on_connection_complete(const RawAddress & address)184 void on_connection_complete(const RawAddress& address) {
185 LOG_INFO("Le connection completed to device:%s", address.ToString().c_str());
186
187 remove_all_clients_with_pending_connections(address);
188 }
189
190 /** Reset bg device list. If called after controller reset, set |after_reset| to
191 * true, as there is no need to wipe controller acceptlist in this case. */
reset(bool after_reset)192 void reset(bool after_reset) {
193 bgconn_dev.clear();
194 if (!after_reset) BTM_AcceptlistClear();
195 }
196
wl_direct_connect_timeout_cb(uint8_t app_id,const RawAddress & address)197 void wl_direct_connect_timeout_cb(uint8_t app_id, const RawAddress& address) {
198 on_connection_timed_out(app_id, address);
199
200 // TODO: this would free the timer, from within the timer callback, which is
201 // bad.
202 direct_connect_remove(app_id, address);
203 }
204
205 /** Add a device to the direcgt connection list. Returns true if device
206 * added to the list, false otherwise */
direct_connect_add(uint8_t app_id,const RawAddress & address)207 bool direct_connect_add(uint8_t app_id, const RawAddress& address) {
208 if (bluetooth::shim::is_gd_l2cap_enabled()) {
209 return L2CA_ConnectFixedChnl(L2CAP_ATT_CID, address);
210 }
211
212 bool in_acceptlist = false;
213 auto it = bgconn_dev.find(address);
214 if (it != bgconn_dev.end()) {
215 // app already trying to connect to this particular device
216 if (it->second.doing_direct_conn.count(app_id)) {
217 LOG(INFO) << "direct connect attempt from app_id=" << loghex(app_id)
218 << " already in progress";
219 return false;
220 }
221
222 // are we already in the acceptlist ?
223 if (anyone_connecting(it)) {
224 LOG_WARN("Background connection attempt already in progress app_id=%x",
225 app_id);
226 in_acceptlist = true;
227 }
228 }
229
230 bool params_changed = BTM_SetLeConnectionModeToFast();
231
232 if (!in_acceptlist) {
233 if (!BTM_AcceptlistAdd(address)) {
234 // if we can't add to acceptlist, turn parameters back to slow.
235 LOG_WARN("Unable to add le device to acceptlist");
236 if (params_changed) BTM_SetLeConnectionModeToSlow();
237 return false;
238 }
239 }
240
241 // Setup a timer
242 alarm_t* timeout = alarm_new("wl_conn_params_30s");
243 alarm_set_closure(
244 FROM_HERE, timeout, DIRECT_CONNECT_TIMEOUT,
245 base::BindOnce(&wl_direct_connect_timeout_cb, app_id, address));
246
247 bgconn_dev[address].doing_direct_conn.emplace(
248 app_id, unique_alarm_ptr(timeout, &alarm_free));
249 return true;
250 }
251
any_direct_connect_left()252 static bool any_direct_connect_left() {
253 for (const auto& tmp : bgconn_dev) {
254 if (!tmp.second.doing_direct_conn.empty()) return true;
255 }
256 return false;
257 }
258
direct_connect_remove(uint8_t app_id,const RawAddress & address)259 bool direct_connect_remove(uint8_t app_id, const RawAddress& address) {
260 auto it = bgconn_dev.find(address);
261 if (it == bgconn_dev.end()) {
262 LOG_WARN("Unable to find background connection to remove");
263 return false;
264 }
265
266 auto app_it = it->second.doing_direct_conn.find(app_id);
267 if (app_it == it->second.doing_direct_conn.end()) {
268 LOG_WARN("Unable to find direct connection to remove");
269 return false;
270 }
271
272 // this will free the alarm
273 it->second.doing_direct_conn.erase(app_it);
274
275 // if we removed last direct connection, lower the scan parameters used for
276 // connecting
277 if (!any_direct_connect_left()) {
278 BTM_SetLeConnectionModeToSlow();
279 }
280
281 if (anyone_connecting(it)) {
282 return true;
283 }
284
285 // no more apps interested - remove from acceptlist
286 BTM_AcceptlistRemove(address);
287 bgconn_dev.erase(it);
288 return true;
289 }
290
dump(int fd)291 void dump(int fd) {
292 dprintf(fd, "\nconnection_manager state:\n");
293 if (bgconn_dev.empty()) {
294 dprintf(fd, "\tno Low Energy connection attempts\n");
295 return;
296 }
297
298 dprintf(fd, "\tdevices attempting connection: %d", (int)bgconn_dev.size());
299 for (const auto& entry : bgconn_dev) {
300 dprintf(fd, "\n\t * %s: ", entry.first.ToString().c_str());
301
302 if (!entry.second.doing_direct_conn.empty()) {
303 dprintf(fd, "\n\t\tapps doing direct connect: ");
304 for (const auto& id : entry.second.doing_direct_conn) {
305 dprintf(fd, "%d, ", id.first);
306 }
307 }
308
309 if (!entry.second.doing_bg_conn.empty()) {
310 dprintf(fd, "\n\t\tapps doing background connect: ");
311 for (const auto& id : entry.second.doing_bg_conn) {
312 dprintf(fd, "%d, ", id);
313 }
314 }
315 }
316 dprintf(fd, "\n");
317 }
318
319 } // namespace connection_manager
320