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