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 "stack/btm/btm_ble_bgconn.h"
33 #include "stack/include/l2c_api.h"
34 
35 #define DIRECT_CONNECT_TIMEOUT (30 * 1000) /* 30 seconds */
36 
37 struct closure_data {
38   base::OnceClosure user_task;
39   base::Location posted_from;
40 };
41 
alarm_closure_cb(void * p)42 static void alarm_closure_cb(void* p) {
43   closure_data* data = (closure_data*)p;
44   VLOG(1) << "executing timer scheduled at %s" << data->posted_from.ToString();
45   std::move(data->user_task).Run();
46   delete data;
47 }
48 
49 // 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)50 void alarm_set_closure(const base::Location& posted_from, alarm_t* alarm,
51                        uint64_t interval_ms, base::OnceClosure user_task) {
52   closure_data* data = new closure_data;
53   data->posted_from = posted_from;
54   data->user_task = std::move(user_task);
55   VLOG(1) << "scheduling timer %s" << data->posted_from.ToString();
56   alarm_set_on_mloop(alarm, interval_ms, alarm_closure_cb, data);
57 }
58 
59 using unique_alarm_ptr = std::unique_ptr<alarm_t, decltype(&alarm_free)>;
60 
61 namespace connection_manager {
62 
63 struct tAPPS_CONNECTING {
64   // ids of clients doing background connection to given device
65   std::set<tAPP_ID> doing_bg_conn;
66 
67   // Apps trying to do direct connection.
68   std::map<tAPP_ID, unique_alarm_ptr> doing_direct_conn;
69 };
70 
71 namespace {
72 // Maps address to apps trying to connect to it
73 std::map<RawAddress, tAPPS_CONNECTING> bgconn_dev;
74 
anyone_connecting(const std::map<RawAddress,tAPPS_CONNECTING>::iterator it)75 bool anyone_connecting(
76     const std::map<RawAddress, tAPPS_CONNECTING>::iterator it) {
77   return (!it->second.doing_bg_conn.empty() ||
78           !it->second.doing_direct_conn.empty());
79 }
80 
81 }  // namespace
82 
83 /** background connection device from the list. Returns pointer to the device
84  * record, or nullptr if not found */
get_apps_connecting_to(const RawAddress & address)85 std::set<tAPP_ID> get_apps_connecting_to(const RawAddress& address) {
86   auto it = bgconn_dev.find(address);
87   return (it != bgconn_dev.end()) ? it->second.doing_bg_conn
88                                   : std::set<tAPP_ID>();
89 }
90 
91 /** Add a device from the background connection list.  Returns true if device
92  * added to the list, or already in list, false otherwise */
background_connect_add(uint8_t app_id,const RawAddress & address)93 bool background_connect_add(uint8_t app_id, const RawAddress& address) {
94   if (bluetooth::shim::is_gd_shim_enabled()) {
95     return L2CA_ConnectFixedChnl(L2CAP_ATT_CID, address);
96   }
97 
98   auto it = bgconn_dev.find(address);
99   bool in_white_list = false;
100   if (it != bgconn_dev.end()) {
101     // device already in the whitelist, just add interested app to the list
102     if (it->second.doing_bg_conn.count(app_id)) {
103       LOG(INFO) << "App id=" << loghex(app_id)
104                 << "already doing background connection to " << address;
105       return true;
106     }
107 
108     // Already in white list ?
109     if (anyone_connecting(it)) {
110       in_white_list = true;
111     }
112   }
113 
114   if (!in_white_list) {
115     // the device is not in the whitelist
116     if (!BTM_WhiteListAdd(address)) return false;
117   }
118 
119   // create endtry for address, and insert app_id.
120   bgconn_dev[address].doing_bg_conn.insert(app_id);
121   return true;
122 }
123 
124 /** Removes all registrations for connection for given device.
125  * Returns true if anything was removed, false otherwise */
remove_unconditional(const RawAddress & address)126 bool remove_unconditional(const RawAddress& address) {
127   auto it = bgconn_dev.find(address);
128   if (it == bgconn_dev.end()) return false;
129 
130   BTM_WhiteListRemove(address);
131   bgconn_dev.erase(it);
132   return true;
133 }
134 
135 /** Remove device from the background connection device list or listening to
136  * advertising list.  Returns true if device was on the list and was succesfully
137  * removed */
background_connect_remove(uint8_t app_id,const RawAddress & address)138 bool background_connect_remove(uint8_t app_id, const RawAddress& address) {
139   VLOG(2) << __func__;
140   auto it = bgconn_dev.find(address);
141   if (it == bgconn_dev.end()) return false;
142 
143   if (!it->second.doing_bg_conn.erase(app_id)) return false;
144 
145   if (anyone_connecting(it)) return true;
146 
147   // no more apps interested - remove from whitelist and delete record
148   BTM_WhiteListRemove(address);
149   bgconn_dev.erase(it);
150   return true;
151 }
152 
153 /** deregister all related background connetion device. */
on_app_deregistered(uint8_t app_id)154 void on_app_deregistered(uint8_t app_id) {
155   auto it = bgconn_dev.begin();
156   auto end = bgconn_dev.end();
157   /* update the BG conn device list */
158   while (it != end) {
159     it->second.doing_bg_conn.erase(app_id);
160 
161     it->second.doing_direct_conn.erase(app_id);
162 
163     if (anyone_connecting(it)) {
164       it++;
165       continue;
166     }
167 
168     BTM_WhiteListRemove(it->first);
169     it = bgconn_dev.erase(it);
170   }
171 }
172 
on_connection_complete(const RawAddress & address)173 void on_connection_complete(const RawAddress& address) {
174   VLOG(2) << __func__;
175   auto it = bgconn_dev.find(address);
176 
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 
184 /** Reset bg device list. If called after controller reset, set |after_reset| to
185  * true, as there is no need to wipe controller white list in this case. */
reset(bool after_reset)186 void reset(bool after_reset) {
187   bgconn_dev.clear();
188   if (!after_reset) BTM_WhiteListClear();
189 }
190 
wl_direct_connect_timeout_cb(uint8_t app_id,const RawAddress & address)191 void wl_direct_connect_timeout_cb(uint8_t app_id, const RawAddress& address) {
192   on_connection_timed_out(app_id, address);
193 
194   // TODO: this would free the timer, from within the timer callback, which is
195   // bad.
196   direct_connect_remove(app_id, address);
197 }
198 
199 /** Add a device to the direcgt connection list.  Returns true if device
200  * added to the list, false otherwise */
direct_connect_add(uint8_t app_id,const RawAddress & address)201 bool direct_connect_add(uint8_t app_id, const RawAddress& address) {
202   if (bluetooth::shim::is_gd_shim_enabled()) {
203     return L2CA_ConnectFixedChnl(L2CAP_ATT_CID, address);
204   }
205   auto it = bgconn_dev.find(address);
206   bool in_white_list = false;
207 
208   if (it != bgconn_dev.end()) {
209     // app already trying to connect to this particular device
210     if (it->second.doing_direct_conn.count(app_id)) {
211       LOG(INFO) << "direct connect attempt from app_id=" << loghex(app_id)
212                 << " already in progress";
213       return false;
214     }
215 
216     // are we already in the white list ?
217     if (anyone_connecting(it)) {
218       in_white_list = true;
219     }
220   }
221 
222   bool params_changed = BTM_SetLeConnectionModeToFast();
223 
224   if (!in_white_list) {
225     if (!BTM_WhiteListAdd(address)) {
226       // if we can't add to white list, turn parameters back to slow.
227       if (params_changed) BTM_SetLeConnectionModeToSlow();
228       return false;
229     }
230   }
231 
232   // Setup a timer
233   alarm_t* timeout = alarm_new("wl_conn_params_30s");
234   alarm_set_closure(
235       FROM_HERE, timeout, DIRECT_CONNECT_TIMEOUT,
236       base::BindOnce(&wl_direct_connect_timeout_cb, app_id, address));
237 
238   bgconn_dev[address].doing_direct_conn.emplace(
239       app_id, unique_alarm_ptr(timeout, &alarm_free));
240   return true;
241 }
242 
any_direct_connect_left()243 bool any_direct_connect_left() {
244   for (const auto& tmp : bgconn_dev) {
245     if (!tmp.second.doing_direct_conn.empty()) return true;
246   }
247   return false;
248 }
249 
direct_connect_remove(uint8_t app_id,const RawAddress & address)250 bool direct_connect_remove(uint8_t app_id, const RawAddress& address) {
251   VLOG(2) << __func__ << ": "
252           << "app_id: " << +app_id << ", address:" << address;
253   auto it = bgconn_dev.find(address);
254   if (it == bgconn_dev.end()) return false;
255 
256   auto app_it = it->second.doing_direct_conn.find(app_id);
257   if (app_it == it->second.doing_direct_conn.end()) return false;
258 
259   // this will free the alarm
260   it->second.doing_direct_conn.erase(app_it);
261 
262   // if we removed last direct connection, lower the scan parameters used for
263   // connecting
264   if (!any_direct_connect_left()) {
265     BTM_SetLeConnectionModeToSlow();
266   }
267 
268   if (anyone_connecting(it)) return true;
269 
270   // no more apps interested - remove from whitelist
271   BTM_WhiteListRemove(address);
272   bgconn_dev.erase(it);
273   return true;
274 }
275 
dump(int fd)276 void dump(int fd) {
277   dprintf(fd, "\nconnection_manager state:\n");
278   if (bgconn_dev.empty()) {
279     dprintf(fd, "\tno Low Energy connection attempts\n");
280     return;
281   }
282 
283   dprintf(fd, "\tdevices attempting connection: %d", (int)bgconn_dev.size());
284   for (const auto& entry : bgconn_dev) {
285     dprintf(fd, "\n\t * %s: ", entry.first.ToString().c_str());
286 
287     if (!entry.second.doing_direct_conn.empty()) {
288       dprintf(fd, "\n\t\tapps doing direct connect: ");
289       for (const auto& id : entry.second.doing_direct_conn) {
290         dprintf(fd, "%d, ", id.first);
291       }
292     }
293 
294     if (!entry.second.doing_bg_conn.empty()) {
295       dprintf(fd, "\n\t\tapps doing background connect: ");
296       for (const auto& id : entry.second.doing_bg_conn) {
297         dprintf(fd, "%d, ", id);
298       }
299     }
300   }
301   dprintf(fd, "\n");
302 }
303 
304 }  // namespace connection_manager
305