"classic_fixed_channel.cc",
"classic_fixed_channel_manager.cc",
"classic_fixed_channel_service.cc",
- "internal/classic_fixed_channel_service_manager_impl.cc",
"internal/classic_fixed_channel_allocator.cc",
+ "internal/classic_fixed_channel_impl.cc",
+ "internal/classic_fixed_channel_service_manager_impl.cc",
"internal/classic_link_manager.cc",
"internal/scheduler_fifo.cc",
],
name: "BluetoothL2capTestSources",
srcs: [
"l2cap_packet_test.cc",
- "internal/classic_fixed_channel_service_manager_test.cc",
"internal/classic_fixed_channel_allocator_test.cc",
+ "internal/classic_fixed_channel_impl_test.cc",
+ "internal/classic_fixed_channel_service_manager_test.cc",
"internal/classic_link_manager_test.cc",
"internal/scheduler_fifo_test.cc",
"signal_id_test.cc",
*/
#include "l2cap/classic_fixed_channel.h"
+#include "common/bind.h"
+#include "l2cap/internal/classic_fixed_channel_impl.h"
namespace bluetooth {
namespace l2cap {
-void ClassicFixedChannel::RegisterOnCloseCallback(os::Handler* handler, OnCloseCallback callback) {}
-void ClassicFixedChannel::Acquire() {}
-void ClassicFixedChannel::Release() {}
+
+hci::Address ClassicFixedChannel::GetDevice() const {
+ return impl_->GetDevice();
+}
+
+void ClassicFixedChannel::RegisterOnCloseCallback(os::Handler* user_handler,
+ ClassicFixedChannel::OnCloseCallback on_close_callback) {
+ l2cap_handler_->Post(common::BindOnce(&internal::ClassicFixedChannelImpl::RegisterOnCloseCallback, impl_,
+ user_handler, std::move(on_close_callback)));
+}
+
+void ClassicFixedChannel::Acquire() {
+ l2cap_handler_->Post(common::BindOnce(&internal::ClassicFixedChannelImpl::Acquire, impl_));
+}
+
+void ClassicFixedChannel::Release() {
+ l2cap_handler_->Post(common::BindOnce(&internal::ClassicFixedChannelImpl::Release, impl_));
+}
+
common::BidiQueueEnd<packet::PacketView<packet::kLittleEndian>, packet::BasePacketBuilder>*
ClassicFixedChannel::GetQueueUpEnd() const {
return nullptr;
#include "common/bidi_queue.h"
#include "common/callback.h"
+#include "hci/acl_manager.h"
+#include "l2cap/cid.h"
#include "os/handler.h"
#include "packet/base_packet_builder.h"
#include "packet/packet_view.h"
/**
* L2CAP fixed channel object. When a new object is created, it must be
* acquired through calling {@link FixedChannel#Acquire()} within X seconds.
- * Otherwise, {@link FixeChannel#Release()} will be called automatically.
+ * Otherwise, {@link FixedChannel#Release()} will be called automatically.
*
*/
class ClassicFixedChannel {
public:
- using OnCloseCallback = common::Callback<void()>;
+ // Should only be constructed by modules that have access to ClassicLinkManager
+ ClassicFixedChannel(std::shared_ptr<internal::ClassicFixedChannelImpl> impl, os::Handler* l2cap_handler)
+ : impl_(std::move(impl)), l2cap_handler_(l2cap_handler) {
+ ASSERT(impl_ != nullptr);
+ ASSERT(l2cap_handler_ != nullptr);
+ }
+
+ hci::Address GetDevice() const;
/**
* Register close callback. If close callback is registered, when a channel is closed, the channel's resource will
* only be freed after on_close callback is invoked. Otherwise, if no on_close callback is registered, the channel's
* resource will be freed immediately after closing.
*
- * @param on_close The callback invoked upon channel closing.
+ * @param user_handler The handler used to invoke the callback on
+ * @param on_close_callback The callback invoked upon channel closing.
*/
- void RegisterOnCloseCallback(os::Handler* handler, OnCloseCallback on_close);
+ using OnCloseCallback = common::OnceCallback<void(hci::ErrorCode)>;
+ void RegisterOnCloseCallback(os::Handler* user_handler, OnCloseCallback on_close_callback);
/**
- * Indicate that this Fixed Channel is being used. This will prevent ACL
- * connection from being disconnected.
+ * Indicate that this Fixed Channel is being used. This will prevent ACL connection from being disconnected.
*/
void Acquire();
/**
- * Indicate that this Fixed Channel is no longer being used. ACL connection
- * will be disconnected after X seconds if no other DynamicChannel is connected
- * or no other Fixed Channel is using this ACL connection. However a module can
- * still receive data on this channel as long as it remains open.
+ * Indicate that this Fixed Channel is no longer being used. ACL connection will be disconnected after
+ * kClassicLinkIdleDisconnectTimeout if no other DynamicChannel is connected or no other Fixed Channel is using this
+ * ACL connection. However a module can still receive data on this channel as long as it remains open.
*/
void Release();
*/
common::BidiQueueEnd<packet::PacketView<packet::kLittleEndian>, packet::BasePacketBuilder>* GetQueueUpEnd() const;
- friend class internal::ClassicFixedChannelImpl;
-
private:
- ClassicFixedChannel(os::Handler* l2cap_handler, internal::ClassicFixedChannelImpl* classic_fixed_channel_impl)
- : l2cap_handler_(l2cap_handler), classic_fixed_channel_impl_(classic_fixed_channel_impl) {}
+ std::shared_ptr<internal::ClassicFixedChannelImpl> impl_;
os::Handler* l2cap_handler_;
- internal::ClassicFixedChannelImpl* classic_fixed_channel_impl_;
- DISALLOW_COPY_AND_ASSIGN(ClassicFixedChannel);
};
} // namespace l2cap
+++ /dev/null
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * 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.
- */
-#pragma once
-
-#include <chrono>
-#include <cstddef>
-
-#include "l2cap/cid.h"
-#include "l2cap/signal_id.h"
-
-namespace bluetooth {
-namespace l2cap {
-
-constexpr size_t kChannelQueueCapacity = 10;
-constexpr size_t kLinkQueueCapacity = 10;
-constexpr std::chrono::milliseconds bogus_link_wakeup_time = std::chrono::milliseconds(15000);
-
-constexpr SignalId kInitialSignalId = SignalId(0x80);
-
-// Time after last channels closes before link is torn down
-constexpr auto kLinkDisconnectTimeout = std::chrono::seconds(30);
-
-// TODO(cmanton) Random numbers for now
-constexpr auto kChannelConnectionTimeout = std::chrono::seconds(30);
-constexpr auto kChannelConnectionPendingTimeout = std::chrono::seconds(30);
-constexpr auto kChannelConfigurationTimeout = std::chrono::seconds(30);
-constexpr auto kChannelDisconnectionTimeout = std::chrono::seconds(30);
-
-// The depth of buffering that the signalling channel can handle.
-constexpr auto kSignallingChannelSize = 20;
-
-} // namespace l2cap
-} // namespace bluetooth
#include "classic_fixed_channel_allocator.h"
#include "l2cap/cid.h"
#include "l2cap/internal/classic_fixed_channel_allocator.h"
+#include "l2cap/internal/classic_link.h"
#include "l2cap/security_policy.h"
#include "os/handler.h"
#include "os/log.h"
namespace l2cap {
namespace internal {
-ClassicFixedChannelImpl* ClassicFixedChannelAllocator::AllocateChannel(Cid cid, SecurityPolicy security_policy) {
- ASSERT_LOG(!IsChannelInUse((cid)), "Cid %d is already in use", cid);
+std::shared_ptr<ClassicFixedChannelImpl> ClassicFixedChannelAllocator::AllocateChannel(Cid cid,
+ SecurityPolicy security_policy) {
+ ASSERT_LOG(!IsChannelAllocated((cid)), "Cid 0x%x for device %s is already in use", cid,
+ link_->GetDevice().ToString().c_str());
ASSERT_LOG(cid >= kFirstFixedChannel && cid <= kLastFixedChannel, "Cid %d out of bound", cid);
- channels_.try_emplace(cid, cid, handler_);
- return &channels_.find(cid)->second;
+ auto elem = channels_.try_emplace(cid, std::make_shared<ClassicFixedChannelImpl>(cid, link_, l2cap_handler_));
+ ASSERT_LOG(elem.second, "Failed to create channel for cid 0x%x device %s", cid,
+ link_->GetDevice().ToString().c_str());
+ ASSERT(elem.first->second != nullptr);
+ return elem.first->second;
}
-bool ClassicFixedChannelAllocator::FreeChannel(Cid cid) {
- ASSERT_LOG(IsChannelInUse(cid), "Channel is not in use: cid %d", cid);
+void ClassicFixedChannelAllocator::FreeChannel(Cid cid) {
+ ASSERT_LOG(IsChannelAllocated(cid), "Channel is not in use: cid %d, device %s", cid,
+ link_->GetDevice().ToString().c_str());
channels_.erase(cid);
- return true;
}
-bool ClassicFixedChannelAllocator::IsChannelInUse(Cid cid) const {
+bool ClassicFixedChannelAllocator::IsChannelAllocated(Cid cid) const {
return channels_.find(cid) != channels_.end();
}
-ClassicFixedChannelImpl* ClassicFixedChannelAllocator::FindChannel(Cid cid) {
- ASSERT_LOG(IsChannelInUse(cid), "Channel is not in use: cid %d", cid);
- return &channels_.find(cid)->second;
+std::shared_ptr<ClassicFixedChannelImpl> ClassicFixedChannelAllocator::FindChannel(Cid cid) {
+ ASSERT_LOG(IsChannelAllocated(cid), "Channel is not in use: cid %d, device %s", cid,
+ link_->GetDevice().ToString().c_str());
+ return channels_.find(cid)->second;
}
+
size_t ClassicFixedChannelAllocator::NumberOfChannels() const {
return channels_.size();
}
+
+void ClassicFixedChannelAllocator::OnAclDisconnected(hci::ErrorCode reason) {
+ for (auto& elem : channels_) {
+ elem.second->OnClosed(reason);
+ }
+}
+
+int ClassicFixedChannelAllocator::GetRefCount() {
+ int ref_count = 0;
+ for (auto& elem : channels_) {
+ if (elem.second->IsAcquired()) {
+ ref_count++;
+ }
+ }
+ return ref_count;
+}
+
} // namespace internal
} // namespace l2cap
} // namespace bluetooth
#pragma once
+#include <hci/hci_packets.h>
#include <unordered_map>
#include "l2cap/cid.h"
namespace l2cap {
namespace internal {
+class ClassicLink;
+
// Helper class for keeping channels in a Link. It allocates and frees Channel object, and supports querying whether a
// channel is in use
class ClassicFixedChannelAllocator {
public:
- explicit ClassicFixedChannelAllocator(os::Handler* handler) : handler_(handler) {
- ASSERT(handler_ != nullptr);
+ ClassicFixedChannelAllocator(ClassicLink* link, os::Handler* l2cap_handler)
+ : link_(link), l2cap_handler_(l2cap_handler) {
+ ASSERT(link_ != nullptr);
+ ASSERT(l2cap_handler_ != nullptr);
}
// Allocates a channel. If cid is used, return nullptr. NOTE: The returned ClassicFixedChannelImpl object is still
- // owned by the channel cllocator, NOT the client.
- ClassicFixedChannelImpl* AllocateChannel(Cid cid, SecurityPolicy security_policy);
+ // owned by the channel allocator, NOT the client.
+ std::shared_ptr<ClassicFixedChannelImpl> AllocateChannel(Cid cid, SecurityPolicy security_policy);
- // Frees a channel. If cid doesn't exist, return false
- bool FreeChannel(Cid cid);
+ // Frees a channel. If cid doesn't exist, it will crash
+ void FreeChannel(Cid cid);
- bool IsChannelInUse(Cid cid) const;
+ bool IsChannelAllocated(Cid cid) const;
- ClassicFixedChannelImpl* FindChannel(Cid cid);
+ std::shared_ptr<ClassicFixedChannelImpl> FindChannel(Cid cid);
size_t NumberOfChannels() const;
+ void OnAclDisconnected(hci::ErrorCode hci_status);
+
+ int GetRefCount();
+
private:
- os::Handler* handler_ = nullptr;
- std::unordered_map<Cid, ClassicFixedChannelImpl> channels_;
+ ClassicLink* link_;
+ os::Handler* l2cap_handler_;
+ std::unordered_map<Cid, std::shared_ptr<ClassicFixedChannelImpl>> channels_;
};
} // namespace internal
*/
#include "l2cap/internal/classic_fixed_channel_allocator.h"
+#include "l2cap/internal/classic_link_mock.h"
+#include "l2cap/internal/parameter_provider_mock.h"
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
namespace bluetooth {
namespace l2cap {
namespace internal {
+using testing::MockClassicLink;
+using testing::MockParameterProvider;
+using ::testing::Return;
+
+const hci::Address device{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+
class L2capClassicFixedChannelAllocatorTest : public ::testing::Test {
protected:
void SetUp() override {
thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
handler_ = new os::Handler(thread_);
- channel_allocator_ = std::make_unique<ClassicFixedChannelAllocator>(handler_);
+ mock_parameter_provider_ = new MockParameterProvider();
+ mock_classic_link_ = new MockClassicLink(handler_, mock_parameter_provider_);
+ EXPECT_CALL(*mock_classic_link_, GetDevice()).WillRepeatedly(Return(device));
+ channel_allocator_ = std::make_unique<ClassicFixedChannelAllocator>(mock_classic_link_, handler_);
}
void TearDown() override {
channel_allocator_.reset();
+ delete mock_classic_link_;
+ delete mock_parameter_provider_;
handler_->Clear();
delete handler_;
delete thread_;
os::Thread* thread_{nullptr};
os::Handler* handler_{nullptr};
+ MockParameterProvider* mock_parameter_provider_{nullptr};
+ MockClassicLink* mock_classic_link_{nullptr};
std::unique_ptr<ClassicFixedChannelAllocator> channel_allocator_;
};
TEST_F(L2capClassicFixedChannelAllocatorTest, precondition) {
Cid cid = kFirstFixedChannel;
- EXPECT_FALSE(channel_allocator_->IsChannelInUse(cid));
+ EXPECT_FALSE(channel_allocator_->IsChannelAllocated(cid));
}
TEST_F(L2capClassicFixedChannelAllocatorTest, allocate_and_free_channel) {
Cid cid = kFirstFixedChannel;
- auto* channel = channel_allocator_->AllocateChannel(cid, {});
- EXPECT_TRUE(channel_allocator_->IsChannelInUse(cid));
+ auto channel = channel_allocator_->AllocateChannel(cid, {});
+ EXPECT_TRUE(channel_allocator_->IsChannelAllocated(cid));
EXPECT_EQ(channel, channel_allocator_->FindChannel(cid));
- EXPECT_TRUE(channel_allocator_->FreeChannel(cid));
- EXPECT_FALSE(channel_allocator_->IsChannelInUse(cid));
+ ASSERT_NO_FATAL_FAILURE(channel_allocator_->FreeChannel(cid));
+ EXPECT_FALSE(channel_allocator_->IsChannelAllocated(cid));
}
} // namespace internal
--- /dev/null
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include <unordered_map>
+
+#include "l2cap/cid.h"
+#include "l2cap/internal/classic_fixed_channel_allocator.h"
+#include "l2cap/internal/classic_fixed_channel_impl.h"
+#include "l2cap/internal/classic_link.h"
+#include "l2cap/security_policy.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+ClassicFixedChannelImpl::ClassicFixedChannelImpl(Cid cid, ClassicLink* link, os::Handler* l2cap_handler)
+ : cid_(cid), device_(link->GetDevice()), link_(link), l2cap_handler_(l2cap_handler) {
+ ASSERT_LOG(cid_ >= kFirstFixedChannel && cid_ <= kLastFixedChannel, "Invalid cid: %d", cid_);
+ ASSERT(!device_.IsEmpty());
+ ASSERT(link_ != nullptr);
+ ASSERT(l2cap_handler_ != nullptr);
+}
+
+void ClassicFixedChannelImpl::RegisterOnCloseCallback(os::Handler* user_handler,
+ ClassicFixedChannel::OnCloseCallback on_close_callback) {
+ ASSERT_LOG(user_handler_ == nullptr, "OnCloseCallback can only be registered once");
+ // If channel is already closed, call the callback immediately without saving it
+ if (closed_) {
+ user_handler->Post(common::BindOnce(std::move(on_close_callback), close_reason_));
+ return;
+ }
+ user_handler_ = user_handler;
+ on_close_callback_ = std::move(on_close_callback);
+}
+
+void ClassicFixedChannelImpl::OnClosed(hci::ErrorCode status) {
+ ASSERT_LOG(!closed_, "Device %s Cid 0x%x closed twice, old status 0x%x, new status 0x%x", device_.ToString().c_str(),
+ cid_, static_cast<int>(close_reason_), static_cast<int>(status));
+ closed_ = true;
+ close_reason_ = status;
+ acquired_ = false;
+ link_ = nullptr;
+ l2cap_handler_ = nullptr;
+ if (user_handler_ == nullptr) {
+ return;
+ }
+ // On close callback can only be called once
+ user_handler_->Post(common::BindOnce(std::move(on_close_callback_), status));
+ user_handler_ = nullptr;
+ on_close_callback_.Reset();
+}
+
+void ClassicFixedChannelImpl::Acquire() {
+ ASSERT_LOG(user_handler_ != nullptr, "Must register OnCloseCallback before calling any methods");
+ if (closed_) {
+ LOG_WARN("%s is already closed", ToString().c_str());
+ ASSERT(!acquired_);
+ return;
+ }
+ if (acquired_) {
+ LOG_DEBUG("%s was already acquired", ToString().c_str());
+ return;
+ }
+ acquired_ = true;
+ link_->RefreshRefCount();
+}
+
+void ClassicFixedChannelImpl::Release() {
+ ASSERT_LOG(user_handler_ != nullptr, "Must register OnCloseCallback before calling any methods");
+ if (closed_) {
+ LOG_WARN("%s is already closed", ToString().c_str());
+ ASSERT(!acquired_);
+ return;
+ }
+ if (!acquired_) {
+ LOG_DEBUG("%s was already released", ToString().c_str());
+ return;
+ }
+ acquired_ = false;
+ link_->RefreshRefCount();
+}
+
+} // namespace internal
+} // namespace l2cap
+} // namespace bluetooth
#include "l2cap/cid.h"
#include "l2cap/classic_fixed_channel.h"
+#include "l2cap/internal/classic_fixed_channel_allocator.h"
#include "os/handler.h"
#include "os/log.h"
namespace l2cap {
namespace internal {
+class ClassicLink;
+
class ClassicFixedChannelImpl {
public:
- ClassicFixedChannelImpl(Cid cid, os::Handler* handler) : cid_(cid), handler_(handler) {
- ASSERT_LOG(cid_ >= kFirstFixedChannel && cid_ <= kLastFixedChannel, "Invalid cid: %d", cid_);
- ASSERT(handler_ != nullptr);
+ ClassicFixedChannelImpl(Cid cid, ClassicLink* link, os::Handler* l2cap_handler);
+
+ virtual ~ClassicFixedChannelImpl() = default;
+
+ hci::Address GetDevice() const {
+ return device_;
+ }
+
+ virtual void RegisterOnCloseCallback(os::Handler* user_handler,
+ ClassicFixedChannel::OnCloseCallback on_close_callback);
+
+ virtual void Acquire();
+
+ virtual void Release();
+
+ virtual bool IsAcquired() const {
+ return acquired_;
}
- std::unique_ptr<ClassicFixedChannel> GetChannelInterface() {
- return std::unique_ptr<ClassicFixedChannel>(new ClassicFixedChannel(handler_, this));
+ virtual void OnClosed(hci::ErrorCode status);
+
+ virtual std::string ToString() {
+ std::ostringstream ss;
+ ss << "Device " << device_ << " Cid 0x" << std::hex << cid_;
+ return ss.str();
}
private:
- Cid cid_;
- os::Handler* handler_;
+ // Constructor states
+ // For logging purpose only
+ const Cid cid_;
+ // For logging purpose only
+ const hci::Address device_;
+ // Needed to handle Acquire() and Release()
+ ClassicLink* link_;
+ os::Handler* l2cap_handler_;
+
+ // User supported states
+ os::Handler* user_handler_ = nullptr;
+ ClassicFixedChannel::OnCloseCallback on_close_callback_{};
+
+ // Internal states
+ bool acquired_ = false;
+ bool closed_ = false;
+ hci::ErrorCode close_reason_ = hci::ErrorCode::SUCCESS;
+
+ DISALLOW_COPY_AND_ASSIGN(ClassicFixedChannelImpl);
};
} // namespace internal
--- /dev/null
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * 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.
+ */
+#pragma once
+
+#include "l2cap/internal/classic_fixed_channel_impl.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+namespace testing {
+
+class MockClassicFixedChannelImpl : public ClassicFixedChannelImpl {
+ public:
+ MOCK_METHOD(void, RegisterOnCloseCallback,
+ (os::Handler * user_handler, ClassicFixedChannel::OnCloseCallback on_close_callback), (override));
+ MOCK_METHOD(void, Acquire, (), (override));
+ MOCK_METHOD(void, Release, (), (override));
+ MOCK_METHOD(bool, IsAcquired, (), (override, const));
+ MOCK_METHOD(void, OnClosed, (hci::ErrorCode status), (override));
+};
+
+} // namespace testing
+} // namespace internal
+} // namespace l2cap
+} // namespace bluetooth
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * 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.
+ */
+#include "l2cap/internal/classic_fixed_channel_impl.h"
+
+#include "common/testing/bind_test_util.h"
+#include "l2cap/cid.h"
+#include "l2cap/internal/classic_link_mock.h"
+#include "l2cap/internal/parameter_provider_mock.h"
+#include "os/handler.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+using testing::MockClassicLink;
+using testing::MockParameterProvider;
+using ::testing::Return;
+
+class L2capClassicFixedChannelImplTest : public ::testing::Test {
+ public:
+ static void SyncHandler(os::Handler* handler) {
+ std::promise<void> promise;
+ auto future = promise.get_future();
+ handler->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
+ future.wait_for(std::chrono::milliseconds(3));
+ }
+
+ protected:
+ void SetUp() override {
+ thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+ l2cap_handler_ = new os::Handler(thread_);
+ }
+
+ void TearDown() override {
+ l2cap_handler_->Clear();
+ delete l2cap_handler_;
+ delete thread_;
+ }
+
+ os::Thread* thread_ = nullptr;
+ os::Handler* l2cap_handler_ = nullptr;
+};
+
+TEST_F(L2capClassicFixedChannelImplTest, get_device) {
+ MockParameterProvider mock_parameter_provider;
+ MockClassicLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+ hci::Address device{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+ EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+ ClassicFixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
+ EXPECT_EQ(device, fixed_channel_impl.GetDevice());
+}
+
+TEST_F(L2capClassicFixedChannelImplTest, close_triggers_callback) {
+ MockParameterProvider mock_parameter_provider;
+ MockClassicLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+ hci::Address device{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+ EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+ ClassicFixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
+
+ // Register on close callback
+ auto user_handler = std::make_unique<os::Handler>(thread_);
+ hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
+ fixed_channel_impl.RegisterOnCloseCallback(
+ user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+
+ // Channel closure should trigger such callback
+ fixed_channel_impl.OnClosed(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION);
+ SyncHandler(user_handler.get());
+ EXPECT_EQ(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION, my_status);
+
+ user_handler->Clear();
+}
+
+TEST_F(L2capClassicFixedChannelImplTest, register_callback_after_close_should_call_immediately) {
+ MockParameterProvider mock_parameter_provider;
+ MockClassicLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+ hci::Address device{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+ EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+ ClassicFixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
+
+ // Channel closure should do nothing
+ fixed_channel_impl.OnClosed(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION);
+
+ // Register on close callback should trigger callback immediately
+ auto user_handler = std::make_unique<os::Handler>(thread_);
+ hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
+ fixed_channel_impl.RegisterOnCloseCallback(
+ user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+ SyncHandler(user_handler.get());
+ EXPECT_EQ(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION, my_status);
+
+ user_handler->Clear();
+}
+
+TEST_F(L2capClassicFixedChannelImplTest, close_twice_should_fail) {
+ MockParameterProvider mock_parameter_provider;
+ MockClassicLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+ hci::Address device{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+ EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+ ClassicFixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
+
+ // Register on close callback
+ auto user_handler = std::make_unique<os::Handler>(thread_);
+ hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
+ fixed_channel_impl.RegisterOnCloseCallback(
+ user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+
+ // Channel closure should trigger such callback
+ fixed_channel_impl.OnClosed(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION);
+ SyncHandler(user_handler.get());
+ EXPECT_EQ(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION, my_status);
+
+ // 2nd OnClose() callback should fail
+ EXPECT_DEATH(fixed_channel_impl.OnClosed(hci::ErrorCode::PAGE_TIMEOUT), ".*OnClosed.*");
+
+ user_handler->Clear();
+}
+
+TEST_F(L2capClassicFixedChannelImplTest, multiple_registeration_should_fail) {
+ MockParameterProvider mock_parameter_provider;
+ MockClassicLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+ hci::Address device{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+ EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+ ClassicFixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
+
+ // Register on close callback
+ auto user_handler = std::make_unique<os::Handler>(thread_);
+ hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
+ fixed_channel_impl.RegisterOnCloseCallback(
+ user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+
+ EXPECT_DEATH(fixed_channel_impl.RegisterOnCloseCallback(user_handler.get(),
+ common::BindOnce([](hci::ErrorCode status) { FAIL(); })),
+ ".*RegisterOnCloseCallback.*");
+
+ user_handler->Clear();
+}
+
+TEST_F(L2capClassicFixedChannelImplTest, call_acquire_before_registeration_should_fail) {
+ MockParameterProvider mock_parameter_provider;
+ MockClassicLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+ hci::Address device{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+ EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+ ClassicFixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
+ EXPECT_DEATH(fixed_channel_impl.Acquire(), ".*Acquire.*");
+}
+
+TEST_F(L2capClassicFixedChannelImplTest, call_release_before_registeration_should_fail) {
+ MockParameterProvider mock_parameter_provider;
+ MockClassicLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+ hci::Address device{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+ EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+ ClassicFixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
+ EXPECT_DEATH(fixed_channel_impl.Release(), ".*Release.*");
+}
+
+TEST_F(L2capClassicFixedChannelImplTest, test_acquire_release_channel) {
+ MockParameterProvider mock_parameter_provider;
+ MockClassicLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+ hci::Address device{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+ EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+ ClassicFixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
+
+ // Register on close callback
+ auto user_handler = std::make_unique<os::Handler>(thread_);
+ hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
+ fixed_channel_impl.RegisterOnCloseCallback(
+ user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+
+ // Default should be false
+ EXPECT_FALSE(fixed_channel_impl.IsAcquired());
+
+ // Should be called 2 times after Acquire() and Release()
+ EXPECT_CALL(mock_classic_link, RefreshRefCount()).Times(2);
+
+ fixed_channel_impl.Acquire();
+ EXPECT_TRUE(fixed_channel_impl.IsAcquired());
+
+ fixed_channel_impl.Release();
+ EXPECT_FALSE(fixed_channel_impl.IsAcquired());
+
+ user_handler->Clear();
+}
+
+TEST_F(L2capClassicFixedChannelImplTest, test_acquire_after_close) {
+ MockParameterProvider mock_parameter_provider;
+ MockClassicLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+ hci::Address device{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+ EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+ ClassicFixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
+
+ // Register on close callback
+ auto user_handler = std::make_unique<os::Handler>(thread_);
+ hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
+ fixed_channel_impl.RegisterOnCloseCallback(
+ user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+
+ // Channel closure should trigger such callback
+ fixed_channel_impl.OnClosed(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION);
+ SyncHandler(user_handler.get());
+ EXPECT_EQ(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION, my_status);
+
+ // Release or Acquire after closing should crash
+ EXPECT_CALL(mock_classic_link, RefreshRefCount()).Times(0);
+ EXPECT_FALSE(fixed_channel_impl.IsAcquired());
+ EXPECT_DEATH(fixed_channel_impl.Acquire(), ".*Acquire.*");
+
+ user_handler->Clear();
+}
+
+} // namespace internal
+} // namespace l2cap
+} // namespace bluetooth
#pragma once
+#include <chrono>
#include <memory>
#include "hci/acl_manager.h"
#include "l2cap/internal/classic_fixed_channel_allocator.h"
+#include "l2cap/internal/classic_fixed_channel_impl.h"
+#include "l2cap/internal/parameter_provider.h"
#include "l2cap/internal/scheduler.h"
+#include "os/alarm.h"
namespace bluetooth {
namespace l2cap {
class ClassicLink {
public:
- ClassicLink(os::Handler* l2cap_layer_handler, std::unique_ptr<hci::AclConnection> acl_connection,
- std::unique_ptr<Scheduler> scheduler)
- : handler_(l2cap_layer_handler), acl_connection_(std::move(acl_connection)), scheduler_(std::move(scheduler)) {}
+ ClassicLink(os::Handler* l2cap_handler, std::unique_ptr<hci::AclConnection> acl_connection,
+ std::unique_ptr<Scheduler> scheduler, ParameterProvider* parameter_provider)
+ : l2cap_handler_(l2cap_handler), acl_connection_(std::move(acl_connection)), scheduler_(std::move(scheduler)),
+ parameter_provider_(parameter_provider) {
+ ASSERT(l2cap_handler_ != nullptr);
+ ASSERT(acl_connection_ != nullptr);
+ ASSERT(scheduler_ != nullptr);
+ ASSERT(parameter_provider_ != nullptr);
+ acl_connection_->RegisterDisconnectCallback(
+ common::BindOnce(&ClassicLink::OnAclDisconnected, common::Unretained(this)), l2cap_handler_);
+ link_idle_disconnect_alarm_.Schedule(common::BindOnce(&ClassicLink::Disconnect, common::Unretained(this)),
+ parameter_provider_->GetClassicLinkIdleDisconnectTimeout());
+ }
- friend class ClassicLinkManager;
+ virtual ~ClassicLink() = default;
+
+ inline virtual hci::Address GetDevice() {
+ return acl_connection_->GetAddress();
+ }
+
+ // ACL methods
+
+ virtual void OnAclDisconnected(hci::ErrorCode status) {
+ fixed_channel_allocator_.OnAclDisconnected(status);
+ }
+
+ virtual void Disconnect() {
+ acl_connection_->Disconnect(hci::DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION);
+ }
+
+ // ClassicFixedChannel methods
+
+ virtual std::shared_ptr<ClassicFixedChannelImpl> AllocateFixedChannel(Cid cid, SecurityPolicy security_policy) {
+ return fixed_channel_allocator_.AllocateChannel(cid, security_policy);
+ }
+
+ virtual bool IsFixedChannelAllocated(Cid cid) {
+ return fixed_channel_allocator_.IsChannelAllocated(cid);
+ }
+
+ // Check how many channels are acquired or in use, if zero, start tear down timer, if non-zero, cancel tear down timer
+ virtual void RefreshRefCount() {
+ int ref_count = 0;
+ ref_count += fixed_channel_allocator_.GetRefCount();
+ ASSERT_LOG(ref_count > 0, "ref_count %d is less than 0", ref_count);
+ if (ref_count > 0) {
+ link_idle_disconnect_alarm_.Cancel();
+ } else {
+ link_idle_disconnect_alarm_.Schedule(common::BindOnce(&ClassicLink::Disconnect, common::Unretained(this)),
+ parameter_provider_->GetClassicLinkIdleDisconnectTimeout());
+ }
+ }
private:
- os::Handler* handler_;
- ClassicFixedChannelAllocator fixed_channel_allocator_{handler_};
+ os::Handler* l2cap_handler_;
+ ClassicFixedChannelAllocator fixed_channel_allocator_{this, l2cap_handler_};
std::unique_ptr<hci::AclConnection> acl_connection_;
std::unique_ptr<Scheduler> scheduler_;
+ ParameterProvider* parameter_provider_;
+ os::Alarm link_idle_disconnect_alarm_{l2cap_handler_};
DISALLOW_COPY_AND_ASSIGN(ClassicLink);
};
// Check if all registered services have an allocated channel and allocate one if not already allocated
int num_new_channels = 0;
for (auto& fixed_channel_service : fixed_channel_services) {
- if (link->fixed_channel_allocator_.IsChannelInUse(fixed_channel_service.first)) {
+ if (link->IsFixedChannelAllocated(fixed_channel_service.first)) {
// This channel is already allocated for this link, do not allocated twice
continue;
}
// Allocate channel for newly registered fixed channels
- auto* classic_fixed_channel_impl =
- link->fixed_channel_allocator_.AllocateChannel(fixed_channel_service.first, SecurityPolicy());
- fixed_channel_service.second->NotifyChannelCreation(classic_fixed_channel_impl->GetChannelInterface());
+ auto fixed_channel_impl = link->AllocateFixedChannel(fixed_channel_service.first, SecurityPolicy());
+ fixed_channel_service.second->NotifyChannelCreation(
+ std::make_unique<ClassicFixedChannel>(fixed_channel_impl, l2cap_handler_));
num_new_channels++;
}
// Declare connection failure if no new channels are created
void ClassicLinkManager::OnConnectSuccess(std::unique_ptr<hci::AclConnection> acl_connection) {
// Same link should not be connected twice
- hci::Address address = acl_connection->GetAddress();
- ASSERT_LOG(GetLink(address) == nullptr, "%s is connected twice without disconnection",
+ hci::Address device = acl_connection->GetAddress();
+ ASSERT_LOG(GetLink(device) == nullptr, "%s is connected twice without disconnection",
acl_connection->GetAddress().ToString().c_str());
auto* link_queue_up_end = acl_connection->GetAclQueueEnd();
- links_.try_emplace(address, handler_, std::move(acl_connection), std::make_unique<Fifo>(link_queue_up_end, handler_));
- auto* link = GetLink(address);
+ links_.try_emplace(device, l2cap_handler_, std::move(acl_connection),
+ std::make_unique<Fifo>(link_queue_up_end, l2cap_handler_), parameter_provider_);
+ auto* link = GetLink(device);
// Allocate and distribute channels for all registered fixed channel services
auto fixed_channel_services = service_manager_->GetRegisteredServices();
for (auto& fixed_channel_service : fixed_channel_services) {
- ClassicFixedChannelImpl* classic_fixed_channel_impl =
- link->fixed_channel_allocator_.AllocateChannel(fixed_channel_service.first, SecurityPolicy());
- fixed_channel_service.second->NotifyChannelCreation(classic_fixed_channel_impl->GetChannelInterface());
+ auto fixed_channel_impl = link->AllocateFixedChannel(fixed_channel_service.first, SecurityPolicy());
+ fixed_channel_service.second->NotifyChannelCreation(
+ std::make_unique<ClassicFixedChannel>(fixed_channel_impl, l2cap_handler_));
}
// Remove device from pending links list, if any
- auto pending_link = pending_links_.find(address);
+ auto pending_link = pending_links_.find(device);
if (pending_link == pending_links_.end()) {
// This an incoming connection, exit
return;
auto pending_link = pending_links_.find(device);
if (pending_link == pending_links_.end()) {
// There is no pending link, exit
+ LOG_DEBUG("Connection to %s failed without a pending link", device.ToString().c_str());
return;
}
for (auto& pending_fixed_channel_connection : pending_link->second.pending_fixed_channel_connections_) {
}
void ClassicLinkManager::OnDisconnect(hci::Address device, hci::ErrorCode status) {
+ auto* link = GetLink(device);
+ ASSERT_LOG(link != nullptr, "Device %s is disconnected with reason 0x%x, but not in local database",
+ device.ToString().c_str(), static_cast<uint8_t>(status));
+ link->OnAclDisconnected(status);
links_.erase(device);
}
#include "os/handler.h"
#include "l2cap/classic_fixed_channel_manager.h"
-#include "l2cap/internal/classic_fixed_channel_service_impl.h"
#include "l2cap/internal/classic_fixed_channel_service_manager_impl.h"
#include "l2cap/internal/classic_link.h"
+#include "l2cap/internal/parameter_provider.h"
#include "l2cap/internal/scheduler.h"
#include "hci/acl_manager.h"
class ClassicLinkManager : public hci::ConnectionCallbacks {
public:
- ClassicLinkManager(os::Handler* l2cap_layer_handler, hci::AclManager* acl_manager,
- ClassicFixedChannelServiceManagerImpl* service_manager)
- : handler_(l2cap_layer_handler), acl_manager_(acl_manager), service_manager_(service_manager) {
- acl_manager_->RegisterCallbacks(this, handler_);
+ ClassicLinkManager(os::Handler* l2cap_handler, hci::AclManager* acl_manager,
+ ClassicFixedChannelServiceManagerImpl* service_manager, ParameterProvider* parameter_provider)
+ : l2cap_handler_(l2cap_handler), acl_manager_(acl_manager), service_manager_(service_manager),
+ parameter_provider_(parameter_provider) {
+ acl_manager_->RegisterCallbacks(this, l2cap_handler_);
}
struct PendingFixedChannelConnection {
std::vector<PendingFixedChannelConnection> pending_fixed_channel_connections_;
};
- void ConnectFixedChannelServices(hci::Address device, PendingFixedChannelConnection pending_fixed_channel_connection);
+ // ACL methods
ClassicLink* GetLink(hci::Address device);
void OnConnectSuccess(std::unique_ptr<hci::AclConnection> acl_connection) override;
void OnConnectFail(hci::Address device, hci::ErrorCode reason) override;
void OnDisconnect(hci::Address device, hci::ErrorCode status);
+ // ClassicFixedChannelManager methods
+
+ void ConnectFixedChannelServices(hci::Address device, PendingFixedChannelConnection pending_fixed_channel_connection);
+
private:
// Dependencies
- os::Handler* handler_;
+ os::Handler* l2cap_handler_;
hci::AclManager* acl_manager_;
ClassicFixedChannelServiceManagerImpl* service_manager_;
+ ParameterProvider* parameter_provider_;
// Internal states
std::unordered_map<hci::Address, PendingLink> pending_links_;
#include "l2cap/internal/classic_link_manager.h"
#include <future>
+#include <thread>
#include "common/bind.h"
#include "common/testing/bind_test_util.h"
#include "l2cap/classic_fixed_channel_manager.h"
#include "l2cap/internal/classic_fixed_channel_service_impl_mock.h"
#include "l2cap/internal/classic_fixed_channel_service_manager_impl_mock.h"
+#include "l2cap/internal/parameter_provider_mock.h"
#include "os/handler.h"
#include "os/thread.h"
using ::testing::DoAll;
using testing::MockClassicFixedChannelServiceImpl;
using testing::MockClassicFixedChannelServiceManagerImpl;
+using testing::MockParameterProvider;
using ::testing::Return;
using ::testing::SaveArg;
+constexpr static auto kTestIdleDisconnectTimeoutLong = std::chrono::milliseconds(1000);
+constexpr static auto kTestIdleDisconnectTimeoutShort = std::chrono::milliseconds(30);
+
class L2capClassicLinkManagerTest : public ::testing::Test {
public:
- void OnFailCallback(ClassicFixedChannelManager::ConnectionResult result) {}
-
static void SyncHandler(os::Handler* handler) {
std::promise<void> promise;
auto future = promise.get_future();
void SetUp() override {
thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
l2cap_handler_ = new os::Handler(thread_);
+ mock_parameter_provider_ = new MockParameterProvider;
+ EXPECT_CALL(*mock_parameter_provider_, GetClassicLinkIdleDisconnectTimeout)
+ .WillRepeatedly(Return(kTestIdleDisconnectTimeoutLong));
}
void TearDown() override {
+ delete mock_parameter_provider_;
l2cap_handler_->Clear();
delete l2cap_handler_;
delete thread_;
os::Thread* thread_ = nullptr;
os::Handler* l2cap_handler_ = nullptr;
+ MockParameterProvider* mock_parameter_provider_ = nullptr;
};
TEST_F(L2capClassicLinkManagerTest, connect_fixed_channel_service_without_acl) {
EXPECT_CALL(mock_acl_manager, RegisterCallbacks(_, _))
.WillOnce(DoAll(SaveArg<0>(&hci_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
ClassicLinkManager classic_link_manager(l2cap_handler_, &mock_acl_manager,
- &mock_classic_fixed_channel_service_manager);
+ &mock_classic_fixed_channel_service_manager, mock_parameter_provider_);
EXPECT_EQ(hci_connection_callbacks, &classic_link_manager);
EXPECT_EQ(hci_callback_handler, l2cap_handler_);
EXPECT_CALL(mock_acl_manager, RegisterCallbacks(_, _))
.WillOnce(DoAll(SaveArg<0>(&hci_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
ClassicLinkManager classic_link_manager(l2cap_handler_, &mock_acl_manager,
- &mock_classic_fixed_channel_service_manager);
+ &mock_classic_fixed_channel_service_manager, mock_parameter_provider_);
EXPECT_EQ(hci_connection_callbacks, &classic_link_manager);
EXPECT_EQ(hci_callback_handler, l2cap_handler_);
EXPECT_CALL(mock_acl_manager, RegisterCallbacks(_, _))
.WillOnce(DoAll(SaveArg<0>(&hci_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
ClassicLinkManager classic_link_manager(l2cap_handler_, &mock_acl_manager,
- &mock_classic_fixed_channel_service_manager);
+ &mock_classic_fixed_channel_service_manager, mock_parameter_provider_);
EXPECT_EQ(hci_connection_callbacks, &classic_link_manager);
EXPECT_EQ(hci_callback_handler, l2cap_handler_);
user_handler->Clear();
}
+TEST_F(L2capClassicLinkManagerTest, not_acquiring_channels_should_disconnect_acl_after_timeout) {
+ EXPECT_CALL(*mock_parameter_provider_, GetClassicLinkIdleDisconnectTimeout)
+ .WillRepeatedly(Return(kTestIdleDisconnectTimeoutShort));
+ MockClassicFixedChannelServiceManagerImpl mock_classic_fixed_channel_service_manager;
+ MockAclManager mock_acl_manager;
+ hci::Address device{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+ auto user_handler = std::make_unique<os::Handler>(thread_);
+
+ // Step 1: Verify callback registration with HCI
+ hci::ConnectionCallbacks* hci_connection_callbacks = nullptr;
+ os::Handler* hci_callback_handler = nullptr;
+ EXPECT_CALL(mock_acl_manager, RegisterCallbacks(_, _))
+ .WillOnce(DoAll(SaveArg<0>(&hci_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
+ ClassicLinkManager classic_link_manager(l2cap_handler_, &mock_acl_manager,
+ &mock_classic_fixed_channel_service_manager, mock_parameter_provider_);
+ EXPECT_EQ(hci_connection_callbacks, &classic_link_manager);
+ EXPECT_EQ(hci_callback_handler, l2cap_handler_);
+
+ // Register fake services
+ MockClassicFixedChannelServiceImpl mock_service_1, mock_service_2;
+ std::vector<std::pair<Cid, ClassicFixedChannelServiceImpl*>> results;
+ results.emplace_back(kSmpBrCid, &mock_service_1);
+ results.emplace_back(kConnectionlessCid, &mock_service_2);
+ EXPECT_CALL(mock_classic_fixed_channel_service_manager, GetRegisteredServices()).WillRepeatedly(Return(results));
+
+ // Step 2: Connect to fixed channel without ACL connection should trigger ACL connection process
+ EXPECT_CALL(mock_acl_manager, CreateConnection(device)).Times(1);
+ ClassicLinkManager::PendingFixedChannelConnection pending_fixed_channel_connection{
+ .handler_ = user_handler.get(),
+ .on_fail_callback_ = common::BindOnce([](ClassicFixedChannelManager::ConnectionResult result) { FAIL(); })};
+ classic_link_manager.ConnectFixedChannelServices(device, std::move(pending_fixed_channel_connection));
+
+ // Step 3: ACL connection success event should trigger channel creation for all registered services
+ auto* raw_acl_connection = new MockAclConnection();
+ std::unique_ptr<MockAclConnection> acl_connection(raw_acl_connection);
+ hci::AclConnection::Queue link_queue{10};
+ EXPECT_CALL(*acl_connection, GetAclQueueEnd()).WillRepeatedly((Return(link_queue.GetUpEnd())));
+ EXPECT_CALL(*acl_connection, GetAddress()).WillRepeatedly(Return(device));
+ std::unique_ptr<ClassicFixedChannel> channel_1, channel_2;
+ EXPECT_CALL(mock_service_1, NotifyChannelCreation(_))
+ .WillOnce([&channel_1](std::unique_ptr<ClassicFixedChannel> channel) { channel_1 = std::move(channel); });
+ EXPECT_CALL(mock_service_2, NotifyChannelCreation(_))
+ .WillOnce([&channel_2](std::unique_ptr<ClassicFixedChannel> channel) { channel_2 = std::move(channel); });
+ hci_callback_handler->Post(common::BindOnce(&hci::ConnectionCallbacks::OnConnectSuccess,
+ common::Unretained(hci_connection_callbacks), std::move(acl_connection)));
+ SyncHandler(hci_callback_handler);
+ EXPECT_NE(channel_1, nullptr);
+ EXPECT_NE(channel_2, nullptr);
+ hci::ErrorCode status_1 = hci::ErrorCode::SUCCESS;
+ channel_1->RegisterOnCloseCallback(
+ user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { status_1 = status; }));
+ hci::ErrorCode status_2 = hci::ErrorCode::SUCCESS;
+ channel_2->RegisterOnCloseCallback(
+ user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { status_2 = status; }));
+
+ // Step 4: Leave channel IDLE long enough, they will disconnect
+ EXPECT_CALL(*raw_acl_connection, Disconnect(hci::DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION)).Times(1);
+ std::this_thread::sleep_for(kTestIdleDisconnectTimeoutShort * 1.2);
+
+ // Step 5: Link disconnect will trigger all callbacks
+ classic_link_manager.OnDisconnect(device, hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST);
+ SyncHandler(user_handler.get());
+ EXPECT_EQ(hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST, status_1);
+ EXPECT_EQ(hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST, status_2);
+
+ user_handler->Clear();
+}
+
} // namespace internal
} // namespace l2cap
} // namespace bluetooth
--- /dev/null
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * 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.
+ */
+#pragma once
+
+#include "hci/acl_manager_mock.h"
+#include "hci/address.h"
+#include "l2cap/internal/classic_link.h"
+#include "l2cap/internal/scheduler_mock.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+namespace testing {
+
+using hci::testing::MockAclConnection;
+
+class MockClassicLink : public ClassicLink {
+ public:
+ explicit MockClassicLink(os::Handler* handler, ParameterProvider* parameter_provider)
+ : ClassicLink(handler, std::make_unique<MockAclConnection>(), std::make_unique<MockScheduler>(),
+ parameter_provider){};
+ MOCK_METHOD(hci::Address, GetDevice, (), (override));
+ MOCK_METHOD(void, OnAclDisconnected, (hci::ErrorCode status), (override));
+ MOCK_METHOD(void, Disconnect, (), (override));
+ MOCK_METHOD(std::shared_ptr<ClassicFixedChannelImpl>, AllocateFixedChannel, (Cid cid, SecurityPolicy security_policy),
+ (override));
+ MOCK_METHOD(bool, IsFixedChannelAllocated, (Cid cid), (override));
+ MOCK_METHOD(void, RefreshRefCount, (), (override));
+};
+
+} // namespace testing
+} // namespace internal
+} // namespace l2cap
+} // namespace bluetooth
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <chrono>
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+/**
+ * A class that provide constant parameters to the L2CAP stack
+ *
+ * All methods are virtual so that they can be override in unit tests
+ */
+class ParameterProvider {
+ public:
+ virtual ~ParameterProvider() = default;
+ virtual std::chrono::milliseconds GetClassicLinkIdleDisconnectTimeout() {
+ return std::chrono::seconds(20);
+ }
+};
+
+} // namespace internal
+} // namespace l2cap
+} // namespace bluetooth
--- /dev/null
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * 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.
+ */
+#pragma once
+
+#include "l2cap/internal/parameter_provider.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+namespace testing {
+
+class MockParameterProvider : public ParameterProvider {
+ public:
+ MOCK_METHOD(std::chrono::milliseconds, GetClassicLinkIdleDisconnectTimeout, (), (override));
+};
+
+} // namespace testing
+} // namespace internal
+} // namespace l2cap
+} // namespace bluetooth
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * 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.
+ */
+#pragma once
+
+#include "l2cap/internal/scheduler.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+namespace testing {
+
+using hci::testing::MockAclConnection;
+
+class MockScheduler : public Scheduler {
+ public:
+ MOCK_METHOD(void, AttachChannel, (Cid cid, UpperQueueDownEnd* channel_down_end), (override));
+ MOCK_METHOD(void, DetachChannel, (Cid cid), (override));
+ MOCK_METHOD(LowerQueueUpEnd*, GetLowerQueueUpEnd, (), (override, const));
+};
+
+} // namespace testing
+} // namespace internal
+} // namespace l2cap
+} // namespace bluetooth
\ No newline at end of file
#include "hci/hci_packets.h"
#include "l2cap/internal/classic_fixed_channel_service_manager_impl.h"
#include "l2cap/internal/classic_link_manager.h"
+#include "l2cap/internal/parameter_provider.h"
#include "module.h"
#include "os/handler.h"
#include "os/log.h"
const ModuleFactory L2capLayer::Factory = ModuleFactory([]() { return new L2capLayer(); });
struct L2capLayer::impl {
- impl(os::Handler* handler, hci::AclManager* acl_manager) : handler_(handler), acl_manager_(acl_manager) {}
- os::Handler* handler_;
+ impl(os::Handler* l2cap_handler, hci::AclManager* acl_manager)
+ : l2cap_handler_(l2cap_handler), acl_manager_(acl_manager) {}
+ os::Handler* l2cap_handler_;
hci::AclManager* acl_manager_;
- internal::ClassicFixedChannelServiceManagerImpl classic_fixed_channel_service_manager_impl_{handler_};
- internal::ClassicLinkManager classic_link_manager_{handler_, acl_manager_,
- &classic_fixed_channel_service_manager_impl_};
+ internal::ParameterProvider parameter_provider_;
+ internal::ClassicFixedChannelServiceManagerImpl classic_fixed_channel_service_manager_impl_{l2cap_handler_};
+ internal::ClassicLinkManager classic_link_manager_{
+ l2cap_handler_, acl_manager_, &classic_fixed_channel_service_manager_impl_, ¶meter_provider_};
};
void L2capLayer::ListDependencies(ModuleList* list) {
std::unique_ptr<ClassicFixedChannelManager> L2capLayer::GetClassicFixedChannelManager() {
return std::unique_ptr<ClassicFixedChannelManager>(new ClassicFixedChannelManager(
- &pimpl_->classic_fixed_channel_service_manager_impl_, &pimpl_->classic_link_manager_, pimpl_->handler_));
+ &pimpl_->classic_fixed_channel_service_manager_impl_, &pimpl_->classic_link_manager_, pimpl_->l2cap_handler_));
}
} // namespace l2cap