• 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 #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