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_event_t;
48
49typedef struct {
50  bt_bdaddr_t bda;
51  uint16_t uuid;
52  bool busy;
53  btif_connect_cb_t connect_cb;
54} connect_node_t;
55
56/*******************************************************************************
57 *  Static variables
58 ******************************************************************************/
59
60static list_t* connect_queue;
61
62static const size_t MAX_REASONABLE_REQUESTS = 10;
63
64/*******************************************************************************
65 *  Queue helper functions
66 ******************************************************************************/
67
68static void queue_int_add(connect_node_t* p_param) {
69  if (!connect_queue) {
70    connect_queue = list_new(osi_free);
71    CHECK(connect_queue != NULL);
72  }
73
74  // Sanity check to make sure we're not leaking connection requests
75  CHECK(list_length(connect_queue) < MAX_REASONABLE_REQUESTS);
76
77  for (const list_node_t* node = list_begin(connect_queue);
78       node != list_end(connect_queue); node = list_next(node)) {
79    if (((connect_node_t*)list_node(node))->uuid == p_param->uuid) {
80      LOG_INFO(LOG_TAG, "%s dropping duplicate connect request for uuid: %04x",
81               __func__, p_param->uuid);
82      return;
83    }
84  }
85
86  connect_node_t* p_node = (connect_node_t*)osi_malloc(sizeof(connect_node_t));
87  memcpy(p_node, p_param, sizeof(connect_node_t));
88  list_append(connect_queue, p_node);
89}
90
91static void queue_int_advance() {
92  if (connect_queue && !list_is_empty(connect_queue))
93    list_remove(connect_queue, list_front(connect_queue));
94}
95
96static void queue_int_handle_evt(uint16_t event, char* p_param) {
97  switch (event) {
98    case BTIF_QUEUE_CONNECT_EVT:
99      queue_int_add((connect_node_t*)p_param);
100      break;
101
102    case BTIF_QUEUE_ADVANCE_EVT:
103      queue_int_advance();
104      break;
105  }
106
107  if (stack_manager_get_interface()->get_stack_is_running())
108    btif_queue_connect_next();
109}
110
111/*******************************************************************************
112 *
113 * Function         btif_queue_connect
114 *
115 * Description      Add a new connection to the queue and trigger the next
116 *                  scheduled connection.
117 *
118 * Returns          BT_STATUS_SUCCESS if successful
119 *
120 ******************************************************************************/
121bt_status_t btif_queue_connect(uint16_t uuid, const bt_bdaddr_t* bda,
122                               btif_connect_cb_t connect_cb) {
123  connect_node_t node;
124  memset(&node, 0, sizeof(connect_node_t));
125  memcpy(&node.bda, bda, sizeof(bt_bdaddr_t));
126  node.uuid = uuid;
127  node.connect_cb = connect_cb;
128
129  return btif_transfer_context(queue_int_handle_evt, BTIF_QUEUE_CONNECT_EVT,
130                               (char*)&node, sizeof(connect_node_t), NULL);
131}
132
133/*******************************************************************************
134 *
135 * Function         btif_queue_advance
136 *
137 * Description      Clear the queue's busy status and advance to the next
138 *                  scheduled connection.
139 *
140 * Returns          void
141 *
142 ******************************************************************************/
143void btif_queue_advance() {
144  btif_transfer_context(queue_int_handle_evt, BTIF_QUEUE_ADVANCE_EVT, NULL, 0,
145                        NULL);
146}
147
148// This function dispatches the next pending connect request. It is called from
149// stack_manager when the stack comes up.
150bt_status_t btif_queue_connect_next(void) {
151  if (!connect_queue || list_is_empty(connect_queue)) return BT_STATUS_FAIL;
152
153  connect_node_t* p_head = (connect_node_t*)list_front(connect_queue);
154
155  // If the queue is currently busy, we return success anyway,
156  // since the connection has been queued...
157  if (p_head->busy) return BT_STATUS_SUCCESS;
158
159  p_head->busy = true;
160  return p_head->connect_cb(&p_head->bda, p_head->uuid);
161}
162
163/*******************************************************************************
164 *
165 * Function         btif_queue_release
166 *
167 * Description      Free up all the queue nodes and set the queue head to NULL
168 *
169 * Returns          void
170 *
171 ******************************************************************************/
172void btif_queue_release() {
173  list_free(connect_queue);
174  connect_queue = NULL;
175}
176