• Home
  • History
  • Annotate
  • 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 #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