1/******************************************************************************
2 *
3 *  Copyright (C) 2009-2012 Broadcom Corporation
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/*******************************************************************************
20 *
21 *  Filename:      btif_profile_queue.c
22 *
23 *  Description:   Bluetooth remote device connection queuing implementation.
24 *
25 ******************************************************************************/
26
27#define LOG_TAG "bt_btif_queue"
28
29#include "btif_profile_queue.h"
30
31#include <base/logging.h>
32#include <string.h>
33
34#include "bt_common.h"
35#include "btif_common.h"
36#include "osi/include/allocator.h"
37#include "osi/include/list.h"
38#include "stack_manager.h"
39
40/*******************************************************************************
41 *  Local type definitions
42 ******************************************************************************/
43
44typedef enum {
45  BTIF_QUEUE_CONNECT_EVT,
46  BTIF_QUEUE_ADVANCE_EVT,
47  BTIF_QUEUE_CLEANUP_EVT
48} btif_queue_event_t;
49
50typedef struct {
51  RawAddress bda;
52  uint16_t uuid;
53  bool busy;
54  btif_connect_cb_t connect_cb;
55} connect_node_t;
56
57/*******************************************************************************
58 *  Static variables
59 ******************************************************************************/
60
61static list_t* connect_queue;
62
63static const size_t MAX_REASONABLE_REQUESTS = 10;
64
65/*******************************************************************************
66 *  Queue helper functions
67 ******************************************************************************/
68
69static void queue_int_add(connect_node_t* p_param) {
70  if (!connect_queue) {
71    LOG_INFO(LOG_TAG, "%s: allocating profile queue", __func__);
72    connect_queue = list_new(osi_free);
73    CHECK(connect_queue != NULL);
74  }
75
76  // Sanity check to make sure we're not leaking connection requests
77  CHECK(list_length(connect_queue) < MAX_REASONABLE_REQUESTS);
78
79  for (const list_node_t* node = list_begin(connect_queue);
80       node != list_end(connect_queue); node = list_next(node)) {
81    if (((connect_node_t*)list_node(node))->uuid == p_param->uuid) {
82      LOG_ERROR(LOG_TAG,
83                "%s dropping duplicate connection request UUID=%04X, "
84                "bd_addr=%s, busy=%d",
85                __func__, p_param->uuid, p_param->bda.ToString().c_str(),
86                p_param->busy);
87      return;
88    }
89  }
90
91  LOG_INFO(
92      LOG_TAG, "%s: adding connection request UUID=%04X, bd_addr=%s, busy=%d",
93      __func__, p_param->uuid, p_param->bda.ToString().c_str(), p_param->busy);
94  connect_node_t* p_node = (connect_node_t*)osi_malloc(sizeof(connect_node_t));
95  memcpy(p_node, p_param, sizeof(connect_node_t));
96  list_append(connect_queue, p_node);
97}
98
99static void queue_int_advance() {
100  if (connect_queue && !list_is_empty(connect_queue)) {
101    connect_node_t* p_head = (connect_node_t*)list_front(connect_queue);
102    LOG_INFO(LOG_TAG,
103             "%s: removing connection request UUID=%04X, bd_addr=%s, busy=%d",
104             __func__, p_head->uuid, p_head->bda.ToString().c_str(),
105             p_head->busy);
106    list_remove(connect_queue, p_head);
107  }
108}
109
110static void queue_int_cleanup(uint16_t* p_uuid) {
111  if (!p_uuid) {
112    LOG_ERROR(LOG_TAG, "%s: UUID is null", __func__);
113    return;
114  }
115  uint16_t uuid = *p_uuid;
116  LOG_INFO(LOG_TAG, "%s: UUID=%04X", __func__, uuid);
117  if (!connect_queue) {
118    return;
119  }
120  connect_node_t* connection_request;
121  const list_node_t* node = list_begin(connect_queue);
122  while (node && node != list_end(connect_queue)) {
123    connection_request = (connect_node_t*)list_node(node);
124    node = list_next(node);
125    if (connection_request->uuid == uuid) {
126      LOG_INFO(LOG_TAG,
127               "%s: removing connection request UUID=%04X, bd_addr=%s, busy=%d",
128               __func__, connection_request->uuid,
129               connection_request->bda.ToString().c_str(),
130               connection_request->busy);
131      list_remove(connect_queue, connection_request);
132    }
133  }
134}
135
136static void queue_int_handle_evt(uint16_t event, char* p_param) {
137  switch (event) {
138    case BTIF_QUEUE_CONNECT_EVT:
139      queue_int_add((connect_node_t*)p_param);
140      break;
141
142    case BTIF_QUEUE_ADVANCE_EVT:
143      queue_int_advance();
144      break;
145
146    case BTIF_QUEUE_CLEANUP_EVT:
147      queue_int_cleanup((uint16_t*)(p_param));
148      return;
149  }
150
151  if (stack_manager_get_interface()->get_stack_is_running())
152    btif_queue_connect_next();
153}
154
155/*******************************************************************************
156 *
157 * Function         btif_queue_connect
158 *
159 * Description      Add a new connection to the queue and trigger the next
160 *                  scheduled connection.
161 *
162 * Returns          BT_STATUS_SUCCESS if successful
163 *
164 ******************************************************************************/
165bt_status_t btif_queue_connect(uint16_t uuid, const RawAddress* bda,
166                               btif_connect_cb_t connect_cb) {
167  connect_node_t node;
168  memset(&node, 0, sizeof(connect_node_t));
169  node.bda = *bda;
170  node.uuid = uuid;
171  node.connect_cb = connect_cb;
172
173  return btif_transfer_context(queue_int_handle_evt, BTIF_QUEUE_CONNECT_EVT,
174                               (char*)&node, sizeof(connect_node_t), NULL);
175}
176
177/*******************************************************************************
178 *
179 * Function         btif_queue_cleanup
180 *
181 * Description      Clean up existing connection requests for a UUID
182 *
183 * Returns          void, always succeed
184 *
185 ******************************************************************************/
186void btif_queue_cleanup(uint16_t uuid) {
187  btif_transfer_context(queue_int_handle_evt, BTIF_QUEUE_CLEANUP_EVT,
188                        (char*)&uuid, sizeof(uint16_t), NULL);
189}
190
191/*******************************************************************************
192 *
193 * Function         btif_queue_advance
194 *
195 * Description      Clear the queue's busy status and advance to the next
196 *                  scheduled connection.
197 *
198 * Returns          void
199 *
200 ******************************************************************************/
201void btif_queue_advance() {
202  btif_transfer_context(queue_int_handle_evt, BTIF_QUEUE_ADVANCE_EVT, NULL, 0,
203                        NULL);
204}
205
206// This function dispatches the next pending connect request. It is called from
207// stack_manager when the stack comes up.
208bt_status_t btif_queue_connect_next(void) {
209  if (!connect_queue || list_is_empty(connect_queue)) return BT_STATUS_FAIL;
210
211  connect_node_t* p_head = (connect_node_t*)list_front(connect_queue);
212
213  LOG_INFO(LOG_TAG,
214           "%s: executing connection request UUID=%04X, bd_addr=%s, busy=%d",
215           __func__, p_head->uuid, p_head->bda.ToString().c_str(),
216           p_head->busy);
217  // If the queue is currently busy, we return success anyway,
218  // since the connection has been queued...
219  if (p_head->busy) return BT_STATUS_SUCCESS;
220
221  p_head->busy = true;
222  return p_head->connect_cb(&p_head->bda, p_head->uuid);
223}
224
225/*******************************************************************************
226 *
227 * Function         btif_queue_release
228 *
229 * Description      Free up all the queue nodes and set the queue head to NULL
230 *
231 * Returns          void
232 *
233 ******************************************************************************/
234void btif_queue_release() {
235  LOG_INFO(LOG_TAG, "%s", __func__);
236  list_free(connect_queue);
237  connect_queue = NULL;
238}
239