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/functional/bind.h>
22 #include <base/functional/callback.h>
23 #include <base/location.h>
24 #include <base/logging.h>
25
26 #include <map>
27 #include <memory>
28 #include <set>
29
30 #include "bind_helpers.h"
31 #include "internal_include/bt_trace.h"
32 #include "main/shim/le_scanning_manager.h"
33 #include "main/shim/shim.h"
34 #include "os/log.h"
35 #include "osi/include/alarm.h"
36 #include "osi/include/log.h"
37 #include "stack/btm/btm_ble_bgconn.h"
38 #include "stack/include/advertise_data_parser.h"
39 #include "stack/include/btm_ble_api.h"
40 #include "stack/include/btu.h" // do_in_main_thread
41 #include "stack/include/l2c_api.h"
42 #include "types/raw_address.h"
43
44 #define DIRECT_CONNECT_TIMEOUT (30 * 1000) /* 30 seconds */
45
46 constexpr char kBtmLogTag[] = "TA";
47
48 struct closure_data {
49 base::OnceClosure user_task;
50 base::Location posted_from;
51 };
52
alarm_closure_cb(void * p)53 static void alarm_closure_cb(void* p) {
54 closure_data* data = (closure_data*)p;
55 VLOG(1) << "executing timer scheduled at %s" << data->posted_from.ToString();
56 std::move(data->user_task).Run();
57 delete data;
58 }
59
60 // 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)61 void alarm_set_closure(const base::Location& posted_from, alarm_t* alarm,
62 uint64_t interval_ms, base::OnceClosure user_task) {
63 closure_data* data = new closure_data;
64 data->posted_from = posted_from;
65 data->user_task = std::move(user_task);
66 VLOG(1) << "scheduling timer %s" << data->posted_from.ToString();
67 alarm_set_on_mloop(alarm, interval_ms, alarm_closure_cb, data);
68 }
69
70 using unique_alarm_ptr = std::unique_ptr<alarm_t, decltype(&alarm_free)>;
71
72 namespace connection_manager {
73
74 struct tAPPS_CONNECTING {
75 // ids of clients doing background connection to given device
76 std::set<tAPP_ID> doing_bg_conn;
77 std::set<tAPP_ID> doing_targeted_announcements_conn;
78 bool is_in_accept_list;
79
80 // Apps trying to do direct connection.
81 std::map<tAPP_ID, unique_alarm_ptr> doing_direct_conn;
82 };
83
84 namespace {
85 // Maps address to apps trying to connect to it
86 std::map<RawAddress, tAPPS_CONNECTING> bgconn_dev;
87
num_of_targeted_announcements_users(void)88 int num_of_targeted_announcements_users(void) {
89 return std::count_if(
90 bgconn_dev.begin(), bgconn_dev.end(), [](const auto& pair) {
91 return (!pair.second.is_in_accept_list &&
92 !pair.second.doing_targeted_announcements_conn.empty());
93 });
94 }
95
is_anyone_interested_to_use_accept_list(const std::map<RawAddress,tAPPS_CONNECTING>::iterator it)96 bool is_anyone_interested_to_use_accept_list(
97 const std::map<RawAddress, tAPPS_CONNECTING>::iterator it) {
98 if (!it->second.doing_targeted_announcements_conn.empty()) {
99 return (!it->second.doing_direct_conn.empty());
100 }
101 return (!it->second.doing_bg_conn.empty() ||
102 !it->second.doing_direct_conn.empty());
103 }
104
is_anyone_connecting(const std::map<RawAddress,tAPPS_CONNECTING>::iterator it)105 bool is_anyone_connecting(
106 const std::map<RawAddress, tAPPS_CONNECTING>::iterator it) {
107 return (!it->second.doing_bg_conn.empty() ||
108 !it->second.doing_direct_conn.empty() ||
109 !it->second.doing_targeted_announcements_conn.empty());
110 }
111
112 } // namespace
113
114 /** background connection device from the list. Returns pointer to the device
115 * record, or nullptr if not found */
get_apps_connecting_to(const RawAddress & address)116 std::set<tAPP_ID> get_apps_connecting_to(const RawAddress& address) {
117 LOG_DEBUG("address=%s", ADDRESS_TO_LOGGABLE_CSTR(address));
118 auto it = bgconn_dev.find(address);
119 return (it != bgconn_dev.end()) ? it->second.doing_bg_conn
120 : std::set<tAPP_ID>();
121 }
122
IsTargetedAnnouncement(const uint8_t * p_eir,uint16_t eir_len)123 bool IsTargetedAnnouncement(const uint8_t* p_eir, uint16_t eir_len) {
124 const uint8_t* p_service_data = p_eir;
125 uint8_t service_data_len = 0;
126
127 while ((p_service_data = AdvertiseDataParser::GetFieldByType(
128 p_service_data + service_data_len,
129 eir_len - (p_service_data - p_eir) - service_data_len,
130 BTM_BLE_AD_TYPE_SERVICE_DATA_TYPE, &service_data_len))) {
131 uint16_t uuid;
132 uint8_t announcement_type;
133 const uint8_t* p_tmp = p_service_data;
134
135 if (service_data_len < 3) {
136 continue;
137 }
138
139 STREAM_TO_UINT16(uuid, p_tmp);
140 LOG_DEBUG("Found UUID 0x%04x", uuid);
141
142 if (uuid != 0x184E && uuid != 0x1853) {
143 continue;
144 }
145
146 STREAM_TO_UINT8(announcement_type, p_tmp);
147 LOG_DEBUG("Found announcement_type 0x%02x", announcement_type);
148 if (announcement_type == 0x01) {
149 return true;
150 }
151 }
152 return false;
153 }
154
155 static void schedule_direct_connect_add(uint8_t app_id,
156 const RawAddress& address);
157
target_announcement_observe_results_cb(tBTM_INQ_RESULTS * p_inq,const uint8_t * p_eir,uint16_t eir_len)158 static void target_announcement_observe_results_cb(tBTM_INQ_RESULTS* p_inq,
159 const uint8_t* p_eir,
160 uint16_t eir_len) {
161 auto addr = p_inq->remote_bd_addr;
162 auto it = bgconn_dev.find(addr);
163 if (it == bgconn_dev.end() ||
164 it->second.doing_targeted_announcements_conn.empty()) {
165 return;
166 }
167
168 if (!IsTargetedAnnouncement(p_eir, eir_len)) {
169 LOG_DEBUG("Not a targeted announcement for device %s",
170 ADDRESS_TO_LOGGABLE_CSTR(addr));
171 return;
172 }
173
174 LOG_INFO("Found targeted announcement for device %s",
175 ADDRESS_TO_LOGGABLE_CSTR(addr));
176
177 if (it->second.is_in_accept_list) {
178 LOG_INFO("Device %s is already connecting", ADDRESS_TO_LOGGABLE_CSTR(addr));
179 return;
180 }
181
182 if (BTM_GetHCIConnHandle(addr, BT_TRANSPORT_LE) != 0xFFFF) {
183 LOG_DEBUG("Device %s already connected", ADDRESS_TO_LOGGABLE_CSTR(addr));
184 return;
185 }
186
187 BTM_LogHistory(kBtmLogTag, addr, "Found TA from");
188
189 /* Take fist app_id and use it for direct_connect */
190 auto app_id = *(it->second.doing_targeted_announcements_conn.begin());
191
192 /* If scan is ongoing lets stop it */
193 do_in_main_thread(FROM_HERE,
194 base::BindOnce(schedule_direct_connect_add, app_id, addr));
195 }
196
target_announcements_filtering_set(bool enable)197 void target_announcements_filtering_set(bool enable) {
198 LOG_DEBUG("enable %d", enable);
199 BTM_LogHistory(kBtmLogTag, RawAddress::kEmpty,
200 (enable ? "Start filtering" : "Stop filtering"));
201
202 /* Safe to call as if there is no support for filtering, this call will be
203 * ignored. */
204 bluetooth::shim::set_target_announcements_filter(enable);
205 BTM_BleTargetAnnouncementObserve(enable,
206 target_announcement_observe_results_cb);
207 }
208
209 /** Add a device to the background connection list for targeted announcements.
210 * Returns
211 * true if device added to the list, or already in list,
212 * false otherwise
213 */
background_connect_targeted_announcement_add(tAPP_ID app_id,const RawAddress & address)214 bool background_connect_targeted_announcement_add(tAPP_ID app_id,
215 const RawAddress& address) {
216 LOG_INFO("app_id=%d, address=%s", static_cast<int>(app_id),
217 ADDRESS_TO_LOGGABLE_CSTR(address));
218
219 bool disable_accept_list = false;
220
221 auto it = bgconn_dev.find(address);
222 if (it != bgconn_dev.end()) {
223 // check if filtering already enabled
224 if (it->second.doing_targeted_announcements_conn.count(app_id)) {
225 LOG_INFO(
226 "app_id=%d, already doing targeted announcement filtering to "
227 "address=%s",
228 static_cast<int>(app_id), ADDRESS_TO_LOGGABLE_CSTR(address));
229 return true;
230 }
231
232 bool targeted_filtering_enabled =
233 !it->second.doing_targeted_announcements_conn.empty();
234
235 // Check if connecting
236 if (!it->second.doing_direct_conn.empty()) {
237 LOG_INFO("app_id=%d, address=%s, already in direct connection",
238 static_cast<int>(app_id), ADDRESS_TO_LOGGABLE_CSTR(address));
239
240 } else if (!targeted_filtering_enabled &&
241 !it->second.doing_bg_conn.empty()) {
242 // device is already in the acceptlist so we would have to remove it
243 LOG_INFO(
244 "already doing background connection to address=%s. Need to disable "
245 "it.",
246 ADDRESS_TO_LOGGABLE_CSTR(address));
247 disable_accept_list = true;
248 }
249 }
250
251 if (disable_accept_list) {
252 BTM_AcceptlistRemove(address);
253 bgconn_dev[address].is_in_accept_list = false;
254 }
255
256 bgconn_dev[address].doing_targeted_announcements_conn.insert(app_id);
257 if (bgconn_dev[address].doing_targeted_announcements_conn.size() == 1) {
258 BTM_LogHistory(kBtmLogTag, address, "Allow connection from");
259 }
260
261 if (num_of_targeted_announcements_users() == 1) {
262 target_announcements_filtering_set(true);
263 }
264
265 return true;
266 }
267
268 /** Add a device from the background connection list. Returns true if device
269 * added to the list, or already in list, false otherwise */
background_connect_add(uint8_t app_id,const RawAddress & address)270 bool background_connect_add(uint8_t app_id, const RawAddress& address) {
271 LOG_DEBUG("app_id=%d, address=%s", static_cast<int>(app_id),
272 ADDRESS_TO_LOGGABLE_CSTR(address));
273 if (bluetooth::shim::is_gd_l2cap_enabled()) {
274 return L2CA_ConnectFixedChnl(L2CAP_ATT_CID, address);
275 }
276
277 auto it = bgconn_dev.find(address);
278 bool in_acceptlist = false;
279 bool is_targeted_announcement_enabled = false;
280 if (it != bgconn_dev.end()) {
281 // device already in the acceptlist, just add interested app to the list
282 if (it->second.doing_bg_conn.count(app_id)) {
283 LOG_DEBUG("app_id=%d, already doing background connection to address=%s",
284 static_cast<int>(app_id), ADDRESS_TO_LOGGABLE_CSTR(address));
285 return true;
286 }
287
288 // Already in acceptlist ?
289 if (it->second.is_in_accept_list) {
290 LOG_DEBUG("app_id=%d, address=%s, already in accept list",
291 static_cast<int>(app_id), ADDRESS_TO_LOGGABLE_CSTR(address));
292 in_acceptlist = true;
293 } else {
294 is_targeted_announcement_enabled =
295 !it->second.doing_targeted_announcements_conn.empty();
296 }
297 }
298
299 if (!in_acceptlist) {
300 // the device is not in the acceptlist
301 if (is_targeted_announcement_enabled) {
302 LOG_DEBUG("Targeted announcement enabled, do not add to AcceptList");
303 } else {
304 if (!BTM_AcceptlistAdd(address)) {
305 LOG_WARN("Failed to add device %s to accept list for app %d",
306 ADDRESS_TO_LOGGABLE_CSTR(address), static_cast<int>(app_id));
307 return false;
308 }
309 bgconn_dev[address].is_in_accept_list = true;
310 }
311 }
312
313 // create entry for address, and insert app_id.
314 // new tAPPS_CONNECTING will be default constructed if not exist
315 bgconn_dev[address].doing_bg_conn.insert(app_id);
316 return true;
317 }
318
319 /** Removes all registrations for connection for given device.
320 * Returns true if anything was removed, false otherwise */
remove_unconditional(const RawAddress & address)321 bool remove_unconditional(const RawAddress& address) {
322 LOG_DEBUG("address=%s", ADDRESS_TO_LOGGABLE_CSTR(address));
323 auto it = bgconn_dev.find(address);
324 if (it == bgconn_dev.end()) {
325 LOG_WARN("address %s is not found", ADDRESS_TO_LOGGABLE_CSTR(address));
326 return false;
327 }
328
329 BTM_AcceptlistRemove(address);
330 bgconn_dev.erase(it);
331 return true;
332 }
333
334 /** Remove device from the background connection device list or listening to
335 * advertising list. Returns true if device was on the list and was
336 * successfully removed */
background_connect_remove(uint8_t app_id,const RawAddress & address)337 bool background_connect_remove(uint8_t app_id, const RawAddress& address) {
338 LOG_DEBUG("app_id=%d, address=%s", static_cast<int>(app_id),
339 ADDRESS_TO_LOGGABLE_CSTR(address));
340 auto it = bgconn_dev.find(address);
341 if (it == bgconn_dev.end()) {
342 LOG_WARN("address %s is not found", ADDRESS_TO_LOGGABLE_CSTR(address));
343 return false;
344 }
345
346 bool accept_list_enabled = it->second.is_in_accept_list;
347 auto num_of_targeted_announcements_before_remove =
348 it->second.doing_targeted_announcements_conn.size();
349
350 bool removed_from_bg_conn = (it->second.doing_bg_conn.erase(app_id) > 0);
351 bool removed_from_ta =
352 (it->second.doing_targeted_announcements_conn.erase(app_id) > 0);
353 if (!removed_from_bg_conn && !removed_from_ta) {
354 LOG_WARN("Failed to remove background connection app %d for address %s",
355 static_cast<int>(app_id), ADDRESS_TO_LOGGABLE_CSTR(address));
356 return false;
357 }
358
359 if (removed_from_ta &&
360 it->second.doing_targeted_announcements_conn.size() == 0) {
361 BTM_LogHistory(kBtmLogTag, address, "Ignore connection from");
362 }
363
364 if (is_anyone_connecting(it)) {
365 LOG_DEBUG("some device is still connecting, app_id=%d, address=%s",
366 static_cast<int>(app_id), ADDRESS_TO_LOGGABLE_CSTR(address));
367 /* Check which method should be used now.*/
368 if (!accept_list_enabled) {
369 /* Accept list was not used */
370 if (!it->second.doing_targeted_announcements_conn.empty()) {
371 /* Keep using filtering */
372 LOG_DEBUG(" Keep using target announcement filtering");
373 } else if (!it->second.doing_bg_conn.empty()) {
374 if (!BTM_AcceptlistAdd(address)) {
375 LOG_WARN("Could not re add device to accept list");
376 } else {
377 bgconn_dev[address].is_in_accept_list = true;
378 }
379 }
380 }
381 return true;
382 }
383
384 bgconn_dev.erase(it);
385
386 // no more apps interested - remove from accept list and delete record
387 if (accept_list_enabled) {
388 BTM_AcceptlistRemove(address);
389 return true;
390 }
391
392 if ((num_of_targeted_announcements_before_remove > 0) &&
393 num_of_targeted_announcements_users() == 0) {
394 target_announcements_filtering_set(true);
395 }
396
397 return true;
398 }
399
is_background_connection(const RawAddress & address)400 bool is_background_connection(const RawAddress& address) {
401 return bgconn_dev.find(address) != bgconn_dev.end();
402 }
403
404 /** deregister all related background connetion device. */
on_app_deregistered(uint8_t app_id)405 void on_app_deregistered(uint8_t app_id) {
406 LOG_DEBUG("app_id=%d", static_cast<int>(app_id));
407 auto it = bgconn_dev.begin();
408 auto end = bgconn_dev.end();
409 /* update the BG conn device list */
410 while (it != end) {
411 it->second.doing_bg_conn.erase(app_id);
412
413 it->second.doing_direct_conn.erase(app_id);
414
415 if (is_anyone_connecting(it)) {
416 it++;
417 continue;
418 }
419
420 BTM_AcceptlistRemove(it->first);
421 it = bgconn_dev.erase(it);
422 }
423 }
424
remove_all_clients_with_pending_connections(const RawAddress & address)425 static void remove_all_clients_with_pending_connections(
426 const RawAddress& address) {
427 LOG_DEBUG("address=%s", ADDRESS_TO_LOGGABLE_CSTR(address));
428 auto it = bgconn_dev.find(address);
429 while (it != bgconn_dev.end() && !it->second.doing_direct_conn.empty()) {
430 uint8_t app_id = it->second.doing_direct_conn.begin()->first;
431 direct_connect_remove(app_id, address);
432 it = bgconn_dev.find(address);
433 }
434 }
435
on_connection_complete(const RawAddress & address)436 void on_connection_complete(const RawAddress& address) {
437 LOG_INFO("Le connection completed to device:%s",
438 ADDRESS_TO_LOGGABLE_CSTR(address));
439
440 remove_all_clients_with_pending_connections(address);
441 }
442
on_connection_timed_out_from_shim(const RawAddress & address)443 void on_connection_timed_out_from_shim(const RawAddress& address) {
444 on_connection_timed_out(0x00, address);
445 }
446
447 /** Reset bg device list. If called after controller reset, set |after_reset|
448 * to true, as there is no need to wipe controller acceptlist in this case. */
reset(bool after_reset)449 void reset(bool after_reset) {
450 bgconn_dev.clear();
451 if (!after_reset) {
452 target_announcements_filtering_set(false);
453 BTM_AcceptlistClear();
454 }
455 }
456
wl_direct_connect_timeout_cb(uint8_t app_id,const RawAddress & address)457 void wl_direct_connect_timeout_cb(uint8_t app_id, const RawAddress& address) {
458 LOG_DEBUG("app_id=%d, address=%s", static_cast<int>(app_id),
459 ADDRESS_TO_LOGGABLE_CSTR(address));
460 on_connection_timed_out(app_id, address);
461
462 // TODO: this would free the timer, from within the timer callback, which is
463 // bad.
464 direct_connect_remove(app_id, address);
465 }
466
467 /** Add a device to the direct connection list. Returns true if device
468 * added to the list, false otherwise */
direct_connect_add(uint8_t app_id,const RawAddress & address)469 bool direct_connect_add(uint8_t app_id, const RawAddress& address) {
470 LOG_DEBUG("app_id=%d, address=%s", static_cast<int>(app_id),
471 ADDRESS_TO_LOGGABLE_CSTR(address));
472 if (bluetooth::shim::is_gd_l2cap_enabled()) {
473 return L2CA_ConnectFixedChnl(L2CAP_ATT_CID, address);
474 }
475
476 bool in_acceptlist = false;
477 auto it = bgconn_dev.find(address);
478 if (it != bgconn_dev.end()) {
479 // app already trying to connect to this particular device
480 if (it->second.doing_direct_conn.count(app_id)) {
481 LOG(INFO) << "direct connect attempt from app_id=" << loghex(app_id)
482 << " already in progress";
483 return false;
484 }
485
486 // are we already in the acceptlist ?
487 if (it->second.is_in_accept_list) {
488 LOG_WARN("Background connection attempt already in progress app_id=%x",
489 app_id);
490 in_acceptlist = true;
491 }
492 }
493
494 if (!in_acceptlist) {
495 if (!BTM_AcceptlistAdd(address, true)) {
496 // if we can't add to acceptlist, turn parameters back to slow.
497 LOG_WARN("Unable to add le device to acceptlist");
498 return false;
499 }
500 bgconn_dev[address].is_in_accept_list = true;
501 }
502
503 // Setup a timer
504 alarm_t* timeout = alarm_new("wl_conn_params_30s");
505 alarm_set_closure(
506 FROM_HERE, timeout, DIRECT_CONNECT_TIMEOUT,
507 base::BindOnce(&wl_direct_connect_timeout_cb, app_id, address));
508
509 bgconn_dev[address].doing_direct_conn.emplace(
510 app_id, unique_alarm_ptr(timeout, &alarm_free));
511
512 return true;
513 }
514
schedule_direct_connect_add(uint8_t app_id,const RawAddress & address)515 static void schedule_direct_connect_add(uint8_t app_id,
516 const RawAddress& address) {
517 direct_connect_add(app_id, address);
518 }
519
direct_connect_remove(uint8_t app_id,const RawAddress & address)520 bool direct_connect_remove(uint8_t app_id, const RawAddress& address) {
521 LOG_DEBUG("app_id=%d, address=%s", static_cast<int>(app_id),
522 ADDRESS_TO_LOGGABLE_CSTR(address));
523 auto it = bgconn_dev.find(address);
524 if (it == bgconn_dev.end()) {
525 LOG_WARN("Unable to find background connection to remove peer:%s",
526 ADDRESS_TO_LOGGABLE_CSTR(address));
527 return false;
528 }
529
530 auto app_it = it->second.doing_direct_conn.find(app_id);
531 if (app_it == it->second.doing_direct_conn.end()) {
532 LOG_WARN("Unable to find direct connection to remove peer:%s",
533 ADDRESS_TO_LOGGABLE_CSTR(address));
534 return false;
535 }
536
537 /* Let see if the device was connected due to Target Announcements.*/
538 bool is_targeted_announcement_enabled =
539 !it->second.doing_targeted_announcements_conn.empty();
540
541 // this will free the alarm
542 it->second.doing_direct_conn.erase(app_it);
543
544 if (is_anyone_interested_to_use_accept_list(it)) {
545 return true;
546 }
547
548 // no more apps interested - remove from acceptlist
549 BTM_AcceptlistRemove(address);
550
551 if (!is_targeted_announcement_enabled) {
552 bgconn_dev.erase(it);
553 } else {
554 it->second.is_in_accept_list = false;
555 }
556
557 return true;
558 }
559
dump(int fd)560 void dump(int fd) {
561 dprintf(fd, "\nconnection_manager state:\n");
562 if (bgconn_dev.empty()) {
563 dprintf(fd, "\tno Low Energy connection attempts\n");
564 return;
565 }
566
567 dprintf(fd, "\tdevices attempting connection: %d", (int)bgconn_dev.size());
568 for (const auto& entry : bgconn_dev) {
569 // TODO: confirm whether we need to replace this
570 dprintf(fd, "\n\t * %s: ", ADDRESS_TO_LOGGABLE_CSTR(entry.first));
571
572 if (!entry.second.doing_direct_conn.empty()) {
573 dprintf(fd, "\n\t\tapps doing direct connect: ");
574 for (const auto& id : entry.second.doing_direct_conn) {
575 dprintf(fd, "%d, ", id.first);
576 }
577 }
578
579 if (!entry.second.doing_bg_conn.empty()) {
580 dprintf(fd, "\n\t\tapps doing background connect: ");
581 for (const auto& id : entry.second.doing_bg_conn) {
582 dprintf(fd, "%d, ", id);
583 }
584 }
585 if (!entry.second.doing_targeted_announcements_conn.empty()) {
586 dprintf(fd, "\n\t\tapps doing cap announcement connect: ");
587 for (const auto& id : entry.second.doing_targeted_announcements_conn) {
588 dprintf(fd, "%d, ", id);
589 }
590 }
591 dprintf(fd, "\n\t\t is in the allow list: %s",
592 entry.second.is_in_accept_list ? "true" : "false");
593 }
594 dprintf(fd, "\n");
595 }
596
597 } // namespace connection_manager
598