• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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/dumpsys.h"
33 #include "main/shim/le_scanning_manager.h"
34 #include "main/shim/shim.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.ToString().c_str());
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 < 1) {
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               addr.ToString().c_str());
171     return;
172   }
173 
174   LOG_INFO("Found targeted announcement for device %s",
175            addr.ToString().c_str());
176 
177   if (it->second.is_in_accept_list) {
178     LOG_INFO("Device %s is already connecting", addr.ToString().c_str());
179     return;
180   }
181 
182   if (BTM_GetHCIConnHandle(addr, BT_TRANSPORT_LE) != 0xFFFF) {
183     LOG_DEBUG("Device %s already connected", addr.ToString().c_str());
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.ToString().c_str());
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.ToString().c_str());
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.ToString().c_str());
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.ToString().c_str());
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.ToString().c_str());
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.ToString().c_str());
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.ToString().c_str());
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.ToString().c_str(), 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.ToString().c_str());
323   auto it = bgconn_dev.find(address);
324   if (it == bgconn_dev.end()) {
325     LOG_WARN("address %s is not found", address.ToString().c_str());
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.ToString().c_str());
340   auto it = bgconn_dev.find(address);
341   if (it == bgconn_dev.end()) {
342     LOG_WARN("address %s is not found", address.ToString().c_str());
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.ToString().c_str());
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.ToString().c_str());
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.ToString().c_str());
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", address.ToString().c_str());
438 
439   remove_all_clients_with_pending_connections(address);
440 }
441 
on_connection_timed_out_from_shim(const RawAddress & address)442 void on_connection_timed_out_from_shim(const RawAddress& address) {
443   on_connection_timed_out(0x00, address);
444 }
445 
446 /** Reset bg device list. If called after controller reset, set |after_reset|
447  * to true, as there is no need to wipe controller acceptlist in this case. */
reset(bool after_reset)448 void reset(bool after_reset) {
449   bgconn_dev.clear();
450   if (!after_reset) {
451     target_announcements_filtering_set(false);
452     BTM_AcceptlistClear();
453   }
454 }
455 
wl_direct_connect_timeout_cb(uint8_t app_id,const RawAddress & address)456 void wl_direct_connect_timeout_cb(uint8_t app_id, const RawAddress& address) {
457   LOG_DEBUG("app_id=%d, address=%s", static_cast<int>(app_id),
458             address.ToString().c_str());
459   on_connection_timed_out(app_id, address);
460 
461   // TODO: this would free the timer, from within the timer callback, which is
462   // bad.
463   direct_connect_remove(app_id, address);
464 }
465 
466 /** Add a device to the direcgt connection list.  Returns true if device
467  * added to the list, false otherwise */
direct_connect_add(uint8_t app_id,const RawAddress & address)468 bool direct_connect_add(uint8_t app_id, const RawAddress& address) {
469   LOG_DEBUG("app_id=%d, address=%s", static_cast<int>(app_id),
470             address.ToString().c_str());
471   if (bluetooth::shim::is_gd_l2cap_enabled()) {
472     return L2CA_ConnectFixedChnl(L2CAP_ATT_CID, address);
473   }
474 
475   bool in_acceptlist = false;
476   auto it = bgconn_dev.find(address);
477   if (it != bgconn_dev.end()) {
478     // app already trying to connect to this particular device
479     if (it->second.doing_direct_conn.count(app_id)) {
480       LOG(INFO) << "direct connect attempt from app_id=" << loghex(app_id)
481                 << " already in progress";
482       return false;
483     }
484 
485     // are we already in the acceptlist ?
486     if (it->second.is_in_accept_list) {
487       LOG_WARN("Background connection attempt already in progress app_id=%x",
488                app_id);
489       in_acceptlist = true;
490     }
491   }
492 
493   bool params_changed = BTM_SetLeConnectionModeToFast();
494 
495   if (!in_acceptlist) {
496     if (!BTM_AcceptlistAdd(address)) {
497       // if we can't add to acceptlist, turn parameters back to slow.
498       LOG_WARN("Unable to add le device to acceptlist");
499       if (params_changed) BTM_SetLeConnectionModeToSlow();
500       return false;
501     }
502     bgconn_dev[address].is_in_accept_list = true;
503   }
504 
505   // Setup a timer
506   alarm_t* timeout = alarm_new("wl_conn_params_30s");
507   alarm_set_closure(
508       FROM_HERE, timeout, DIRECT_CONNECT_TIMEOUT,
509       base::BindOnce(&wl_direct_connect_timeout_cb, app_id, address));
510 
511   bgconn_dev[address].doing_direct_conn.emplace(
512       app_id, unique_alarm_ptr(timeout, &alarm_free));
513 
514   return true;
515 }
516 
schedule_direct_connect_add(uint8_t app_id,const RawAddress & address)517 static void schedule_direct_connect_add(uint8_t app_id,
518                                         const RawAddress& address) {
519   direct_connect_add(app_id, address);
520 }
521 
any_direct_connect_left()522 static bool any_direct_connect_left() {
523   for (const auto& tmp : bgconn_dev) {
524     if (!tmp.second.doing_direct_conn.empty()) return true;
525   }
526   return false;
527 }
528 
direct_connect_remove(uint8_t app_id,const RawAddress & address)529 bool direct_connect_remove(uint8_t app_id, const RawAddress& address) {
530   LOG_DEBUG("app_id=%d, address=%s", static_cast<int>(app_id),
531             address.ToString().c_str());
532   auto it = bgconn_dev.find(address);
533   if (it == bgconn_dev.end()) {
534     LOG_WARN("Unable to find background connection to remove peer:%s",
535              PRIVATE_ADDRESS(address));
536     return false;
537   }
538 
539   auto app_it = it->second.doing_direct_conn.find(app_id);
540   if (app_it == it->second.doing_direct_conn.end()) {
541     LOG_WARN("Unable to find direct connection to remove peer:%s",
542              PRIVATE_ADDRESS(address));
543     return false;
544   }
545 
546   /* Let see if the device was connected due to Target Announcements.*/
547   bool is_targeted_announcement_enabled =
548       !it->second.doing_targeted_announcements_conn.empty();
549 
550   // this will free the alarm
551   it->second.doing_direct_conn.erase(app_it);
552 
553   // if we removed last direct connection, lower the scan parameters used for
554   // connecting
555   if (!any_direct_connect_left()) {
556     BTM_SetLeConnectionModeToSlow();
557   }
558 
559   if (is_anyone_interested_to_use_accept_list(it)) {
560     return true;
561   }
562 
563   // no more apps interested - remove from acceptlist
564   BTM_AcceptlistRemove(address);
565 
566   if (!is_targeted_announcement_enabled) {
567     bgconn_dev.erase(it);
568   } else {
569     it->second.is_in_accept_list = false;
570   }
571 
572   return true;
573 }
574 
dump(int fd)575 void dump(int fd) {
576   dprintf(fd, "\nconnection_manager state:\n");
577   if (bgconn_dev.empty()) {
578     dprintf(fd, "\tno Low Energy connection attempts\n");
579     return;
580   }
581 
582   dprintf(fd, "\tdevices attempting connection: %d", (int)bgconn_dev.size());
583   for (const auto& entry : bgconn_dev) {
584     dprintf(fd, "\n\t * %s: ", entry.first.ToString().c_str());
585 
586     if (!entry.second.doing_direct_conn.empty()) {
587       dprintf(fd, "\n\t\tapps doing direct connect: ");
588       for (const auto& id : entry.second.doing_direct_conn) {
589         dprintf(fd, "%d, ", id.first);
590       }
591     }
592 
593     if (!entry.second.doing_bg_conn.empty()) {
594       dprintf(fd, "\n\t\tapps doing background connect: ");
595       for (const auto& id : entry.second.doing_bg_conn) {
596         dprintf(fd, "%d, ", id);
597       }
598     }
599     if (!entry.second.doing_targeted_announcements_conn.empty()) {
600       dprintf(fd, "\n\t\tapps doing cap announcement connect: ");
601       for (const auto& id : entry.second.doing_targeted_announcements_conn) {
602         dprintf(fd, "%d, ", id);
603       }
604     }
605     dprintf(fd, "\n\t\t is in the allow list: %s",
606             entry.second.is_in_accept_list ? "true" : "false");
607   }
608   dprintf(fd, "\n");
609 }
610 
611 }  // namespace connection_manager
612