/****************************************************************************** * * Copyright 2009-2012 Broadcom Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ /******************************************************************************* * * Filename: btif_profile_queue.c * * Description: Bluetooth remote device connection queuing implementation. * ******************************************************************************/ #define LOG_TAG "bt_btif_queue" #include "btif_profile_queue.h" #include <base/bind.h> #include <base/callback.h> #include <base/logging.h> #include <base/strings/stringprintf.h> #include <string.h> #include <list> #include "bt_common.h" #include "btif_common.h" #include "stack_manager.h" /******************************************************************************* * Local type definitions ******************************************************************************/ // Class to store connect info. class ConnectNode { public: ConnectNode(const RawAddress& address, uint16_t uuid, btif_connect_cb_t connect_cb) : address_(address), uuid_(uuid), busy_(false), connect_cb_(connect_cb) {} std::string ToString() const { return base::StringPrintf("address=%s UUID=%04X busy=%s", address_.ToString().c_str(), uuid_, (busy_) ? "true" : "false"); } const RawAddress& address() const { return address_; } uint16_t uuid() const { return uuid_; } /** * Initiate the connection. * * @return BT_STATUS_SUCCESS on success, othewise the corresponding error * code. Note: if a previous connect request hasn't been completed, the * return value is BT_STATUS_SUCCESS. */ bt_status_t connect() { if (busy_) return BT_STATUS_SUCCESS; busy_ = true; return connect_cb_(&address_, uuid_); } private: RawAddress address_; uint16_t uuid_; bool busy_; btif_connect_cb_t connect_cb_; }; /******************************************************************************* * Static variables ******************************************************************************/ static std::list<ConnectNode> connect_queue; static const size_t MAX_REASONABLE_REQUESTS = 20; /******************************************************************************* * Queue helper functions ******************************************************************************/ static void queue_int_add(uint16_t uuid, const RawAddress& bda, btif_connect_cb_t connect_cb) { // Sanity check to make sure we're not leaking connection requests CHECK(connect_queue.size() < MAX_REASONABLE_REQUESTS); ConnectNode param(bda, uuid, connect_cb); for (const auto& node : connect_queue) { if (node.uuid() == param.uuid() && node.address() == param.address()) { LOG_ERROR(LOG_TAG, "%s: dropping duplicate connection request: %s", __func__, param.ToString().c_str()); return; } } LOG_INFO(LOG_TAG, "%s: adding connection request: %s", __func__, param.ToString().c_str()); connect_queue.push_back(param); btif_queue_connect_next(); } static void queue_int_advance() { if (connect_queue.empty()) return; const ConnectNode& head = connect_queue.front(); LOG_INFO(LOG_TAG, "%s: removing connection request: %s", __func__, head.ToString().c_str()); connect_queue.pop_front(); btif_queue_connect_next(); } static void queue_int_cleanup(uint16_t uuid) { LOG_INFO(LOG_TAG, "%s: UUID=%04X", __func__, uuid); for (auto it = connect_queue.begin(); it != connect_queue.end();) { auto it_prev = it++; const ConnectNode& node = *it_prev; if (node.uuid() == uuid) { LOG_INFO(LOG_TAG, "%s: removing connection request: %s", __func__, node.ToString().c_str()); connect_queue.erase(it_prev); } } } static void queue_int_release() { connect_queue.clear(); } /******************************************************************************* * * Function btif_queue_connect * * Description Add a new connection to the queue and trigger the next * scheduled connection. * * Returns BT_STATUS_SUCCESS if successful * ******************************************************************************/ bt_status_t btif_queue_connect(uint16_t uuid, const RawAddress* bda, btif_connect_cb_t connect_cb) { return do_in_jni_thread(FROM_HERE, base::Bind(&queue_int_add, uuid, *bda, connect_cb)); } /******************************************************************************* * * Function btif_queue_cleanup * * Description Clean up existing connection requests for a UUID * * Returns void, always succeed * ******************************************************************************/ void btif_queue_cleanup(uint16_t uuid) { do_in_jni_thread(FROM_HERE, base::Bind(&queue_int_cleanup, uuid)); } /******************************************************************************* * * Function btif_queue_advance * * Description Clear the queue's busy status and advance to the next * scheduled connection. * * Returns void * ******************************************************************************/ void btif_queue_advance() { do_in_jni_thread(FROM_HERE, base::Bind(&queue_int_advance)); } bt_status_t btif_queue_connect_next(void) { // The call must be on the JNI thread, otherwise the access to connect_queue // is not thread-safe. CHECK(is_on_jni_thread()); if (connect_queue.empty()) return BT_STATUS_FAIL; if (!stack_manager_get_interface()->get_stack_is_running()) return BT_STATUS_FAIL; ConnectNode& head = connect_queue.front(); LOG_INFO(LOG_TAG, "%s: executing connection request: %s", __func__, head.ToString().c_str()); return head.connect(); } /******************************************************************************* * * Function btif_queue_release * * Description Free up all the queue nodes and set the queue head to NULL * * Returns void * ******************************************************************************/ void btif_queue_release() { LOG_INFO(LOG_TAG, "%s", __func__); if (do_in_jni_thread(FROM_HERE, base::Bind(&queue_int_release)) != BT_STATUS_SUCCESS) { LOG(FATAL) << __func__ << ": Failed to schedule on JNI thread"; } }