| /* |
| * Copyright 2020 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 "shim/l2cap.h" |
| |
| #include <gtest/gtest.h> |
| #include <future> |
| #include <memory> |
| |
| #include "common/bind.h" |
| #include "hci/address.h" |
| #include "hci/address_with_type.h" |
| #include "l2cap/classic/dynamic_channel_configuration_option.h" |
| #include "l2cap/classic/dynamic_channel_manager.h" |
| #include "l2cap/classic/internal/dynamic_channel_service_manager_impl_mock.h" |
| #include "l2cap/classic/l2cap_classic_module.h" |
| #include "l2cap/internal/ilink.h" |
| #include "l2cap/psm.h" |
| #include "l2cap/security_policy.h" |
| #include "os/handler.h" |
| |
| namespace bluetooth { |
| namespace shim { |
| namespace { |
| |
| constexpr uint16_t kPsm = 123; |
| constexpr uint16_t kPsm2 = kPsm + 2; |
| constexpr uint16_t kCid = 456; |
| constexpr uint16_t kCid2 = kCid + 1; |
| constexpr char device_address[] = "11:22:33:44:55:66"; |
| constexpr char device_address2[] = "aa:bb:cc:dd:ee:ff"; |
| constexpr bool kNoUseErtm = false; |
| constexpr uint16_t kMtu = 1000; |
| |
| class TestDynamicChannelService : public l2cap::classic::DynamicChannelService { |
| public: |
| TestDynamicChannelService(l2cap::Psm psm, l2cap::classic::internal::DynamicChannelServiceManagerImpl* manager, |
| os::Handler* handler) |
| : DynamicChannelService(psm, manager, handler) {} |
| }; |
| |
| class TestLink : public l2cap::internal::ILink { |
| public: |
| hci::AddressWithType GetDevice() { |
| return device_with_type_; |
| } |
| hci::AddressWithType device_with_type_; |
| |
| void SendLeCredit(l2cap::Cid local_cid, uint16_t credit) {} |
| |
| void SendDisconnectionRequest(l2cap::Cid cid, l2cap::Cid remote_cid) { |
| connection_closed_promise_.set_value(); |
| } |
| std::promise<void> connection_closed_promise_; |
| }; |
| |
| class TestDynamicChannelManagerImpl { |
| public: |
| bool ConnectChannel(hci::Address device, l2cap::classic::DynamicChannelConfigurationOption configuration_option, |
| l2cap::Psm psm, l2cap::classic::DynamicChannelManager::OnConnectionOpenCallback on_open_callback, |
| l2cap::classic::DynamicChannelManager::OnConnectionFailureCallback on_fail_callback, |
| os::Handler* handler) { |
| connections_++; |
| on_open_callback_ = std::move(on_open_callback); |
| on_fail_callback_ = std::move(on_fail_callback); |
| |
| connected_promise_.set_value(); |
| return true; |
| } |
| int connections_{0}; |
| |
| bool RegisterService(l2cap::Psm psm, l2cap::classic::DynamicChannelConfigurationOption configuration_option, |
| const l2cap::SecurityPolicy& security_policy, |
| l2cap::classic::DynamicChannelManager::OnRegistrationCompleteCallback on_registration_complete, |
| l2cap::classic::DynamicChannelManager::OnConnectionOpenCallback on_open_callback, |
| os::Handler* handler) { |
| services_++; |
| on_registration_complete_ = std::move(on_registration_complete); |
| on_open_callback_ = std::move(on_open_callback); |
| |
| register_promise_.set_value(); |
| return true; |
| } |
| int services_{0}; |
| |
| void SetConnectionFuture() { |
| connected_promise_ = std::promise<void>(); |
| } |
| |
| void WaitConnectionFuture() { |
| connected_future_ = connected_promise_.get_future(); |
| connected_future_.wait(); |
| } |
| |
| void SetRegistrationFuture() { |
| register_promise_ = std::promise<void>(); |
| } |
| |
| void WaitRegistrationFuture() { |
| register_future_ = register_promise_.get_future(); |
| register_future_.wait(); |
| } |
| |
| void SetConnectionOnFail(l2cap::classic::DynamicChannelManager::ConnectionResult result, std::promise<void> promise) { |
| std::move(on_fail_callback_).Run(result); |
| promise.set_value(); |
| } |
| |
| void SetConnectionOnOpen(std::unique_ptr<l2cap::DynamicChannel> channel, std::promise<void> promise) { |
| std::move(on_open_callback_).Run(std::move(channel)); |
| promise.set_value(); |
| } |
| |
| l2cap::classic::DynamicChannelManager::OnRegistrationCompleteCallback on_registration_complete_{}; |
| l2cap::classic::DynamicChannelManager::OnConnectionOpenCallback on_open_callback_{}; |
| l2cap::classic::DynamicChannelManager::OnConnectionFailureCallback on_fail_callback_{}; |
| |
| TestDynamicChannelManagerImpl() = default; |
| ~TestDynamicChannelManagerImpl() = default; |
| |
| private: |
| std::promise<void> connected_promise_; |
| std::future<void> connected_future_; |
| |
| std::promise<void> register_promise_; |
| std::future<void> register_future_; |
| }; |
| |
| class TestDynamicChannelManager : public l2cap::classic::DynamicChannelManager { |
| public: |
| bool ConnectChannel(hci::Address device, l2cap::classic::DynamicChannelConfigurationOption configuration_option, |
| l2cap::Psm psm, l2cap::classic::DynamicChannelManager::OnConnectionOpenCallback on_open_callback, |
| l2cap::classic::DynamicChannelManager::OnConnectionFailureCallback on_fail_callback, |
| os::Handler* handler) override { |
| return impl_.ConnectChannel(device, configuration_option, psm, std::move(on_open_callback), |
| std::move(on_fail_callback), handler); |
| } |
| |
| bool RegisterService(l2cap::Psm psm, l2cap::classic::DynamicChannelConfigurationOption configuration_option, |
| const l2cap::SecurityPolicy& security_policy, |
| l2cap::classic::DynamicChannelManager::OnRegistrationCompleteCallback on_registration_complete, |
| l2cap::classic::DynamicChannelManager::OnConnectionOpenCallback on_open_callback, |
| os::Handler* handler) override { |
| return impl_.RegisterService(psm, configuration_option, security_policy, std::move(on_registration_complete), |
| std::move(on_open_callback), handler); |
| } |
| TestDynamicChannelManager(TestDynamicChannelManagerImpl& impl) : impl_(impl) {} |
| TestDynamicChannelManagerImpl& impl_; |
| }; |
| |
| class TestL2capClassicModule : public l2cap::classic::L2capClassicModule { |
| public: |
| std::unique_ptr<l2cap::classic::DynamicChannelManager> GetDynamicChannelManager() override { |
| ASSERT(impl_ != nullptr); |
| return std::make_unique<TestDynamicChannelManager>(*impl_); |
| } |
| |
| void ListDependencies(ModuleList* list) override {} |
| void Start() override; |
| void Stop() override; |
| |
| std::unique_ptr<TestDynamicChannelManagerImpl> impl_; |
| }; |
| |
| void TestL2capClassicModule::Start() { |
| impl_ = std::make_unique<TestDynamicChannelManagerImpl>(); |
| } |
| |
| void TestL2capClassicModule::Stop() { |
| impl_.reset(); |
| } |
| |
| class ShimL2capTest : public ::testing::Test { |
| public: |
| void OnConnectionComplete(std::string string_address, uint16_t psm, uint16_t cid, bool connected) { |
| connection_string_address_ = string_address; |
| connection_psm_ = psm; |
| connection_cid_ = cid; |
| connection_connected_ = connected; |
| connection_complete_promise_.set_value(); |
| } |
| |
| uint16_t CreateConnection(uint16_t psm, std::string device_address) { |
| std::promise<uint16_t> promise; |
| auto future = promise.get_future(); |
| |
| shim_l2cap_->CreateConnection( |
| psm, device_address, |
| std::bind(&bluetooth::shim::ShimL2capTest::OnConnectionComplete, this, std::placeholders::_1, |
| std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), |
| std::move(promise)); |
| return future.get(); |
| } |
| |
| void SetConnectionFuture() { |
| test_l2cap_classic_module_->impl_->SetConnectionFuture(); |
| } |
| |
| void WaitConnectionFuture() { |
| test_l2cap_classic_module_->impl_->WaitConnectionFuture(); |
| } |
| |
| void SetRegistrationFuture() { |
| test_l2cap_classic_module_->impl_->SetRegistrationFuture(); |
| } |
| |
| void WaitRegistrationFuture() { |
| test_l2cap_classic_module_->impl_->WaitRegistrationFuture(); |
| } |
| |
| int NumberOfConnections() const { |
| return test_l2cap_classic_module_->impl_->connections_; |
| } |
| |
| int NumberOfServices() const { |
| return test_l2cap_classic_module_->impl_->services_; |
| } |
| |
| std::string connection_string_address_; |
| uint16_t connection_psm_{0}; |
| uint16_t connection_cid_{0}; |
| bool connection_connected_{false}; |
| |
| shim::L2cap* shim_l2cap_ = nullptr; |
| TestL2capClassicModule* test_l2cap_classic_module_{nullptr}; |
| |
| TestLink test_link_; |
| std::promise<void> connection_complete_promise_; |
| |
| protected: |
| void SetUp() override { |
| handler_ = new os::Handler(&thread_); |
| |
| test_l2cap_classic_module_ = new TestL2capClassicModule(); |
| test_l2cap_classic_module_->Start(); |
| fake_registry_.InjectTestModule(&l2cap::classic::L2capClassicModule::Factory, test_l2cap_classic_module_); |
| |
| fake_registry_.Start<shim::L2cap>(&thread_); |
| shim_l2cap_ = static_cast<shim::L2cap*>(fake_registry_.GetModuleUnderTest(&shim::L2cap::Factory)); |
| } |
| |
| void TearDown() override { |
| fake_registry_.StopAll(); |
| handler_->Clear(); |
| delete handler_; |
| } |
| os::Handler* handler_ = nullptr; |
| l2cap::classic::internal::testing::MockDynamicChannelServiceManagerImpl mock_; |
| |
| private: |
| TestModuleRegistry fake_registry_; |
| os::Thread& thread_ = fake_registry_.GetTestThread(); |
| }; |
| |
| TEST_F(ShimL2capTest, CreateThenDisconnectBeforeCompletion) { |
| SetConnectionFuture(); |
| |
| ASSERT(NumberOfConnections() == 0); |
| uint16_t cid = CreateConnection(kPsm, device_address); |
| ASSERT(cid != 0); |
| |
| WaitConnectionFuture(); |
| ASSERT(NumberOfConnections() == 1); |
| |
| shim_l2cap_->CloseConnection(cid); |
| } |
| |
| TEST_F(ShimL2capTest, MaxCreatedConnections) { |
| for (int i = 0; i < 65536 - 64; i++) { |
| SetConnectionFuture(); |
| uint16_t cid = CreateConnection(kPsm, device_address); |
| ASSERT(cid != 0); |
| WaitConnectionFuture(); |
| |
| ASSERT(NumberOfConnections() == i + 1); |
| } |
| uint16_t cid = CreateConnection(kPsm, device_address); |
| ASSERT(cid == 0); |
| |
| ASSERT(NumberOfConnections() == 65536 - 64); |
| } |
| |
| TEST_F(ShimL2capTest, TwoDifferentCreatedConnections) { |
| { |
| SetConnectionFuture(); |
| uint16_t cid = CreateConnection(kPsm, device_address); |
| ASSERT(cid != 0); |
| WaitConnectionFuture(); |
| |
| ASSERT(NumberOfConnections() == 1); |
| } |
| |
| { |
| SetConnectionFuture(); |
| uint16_t cid = CreateConnection(kPsm2, device_address2); |
| ASSERT(cid != 0); |
| WaitConnectionFuture(); |
| |
| ASSERT(NumberOfConnections() == 2); |
| } |
| } |
| |
| TEST_F(ShimL2capTest, ConnectFail) { |
| SetConnectionFuture(); |
| uint16_t cid = CreateConnection(kPsm, device_address); |
| ASSERT(cid != 0); |
| WaitConnectionFuture(); |
| |
| ASSERT(NumberOfConnections() == 1); |
| |
| l2cap::classic::DynamicChannelManager::ConnectionResult result{ |
| .connection_result_code = TestDynamicChannelManager::ConnectionResultCode::FAIL_NO_SERVICE_REGISTERED, |
| .hci_error = hci::ErrorCode::SUCCESS, |
| .l2cap_connection_response_result = l2cap::ConnectionResponseResult::SUCCESS, |
| }; |
| |
| std::promise<void> on_fail_promise; |
| auto on_fail_future = on_fail_promise.get_future(); |
| handler_->Post(common::BindOnce(&TestDynamicChannelManagerImpl::SetConnectionOnFail, |
| common::Unretained(test_l2cap_classic_module_->impl_.get()), result, |
| std::move(on_fail_promise))); |
| on_fail_future.wait(); |
| |
| ASSERT(connection_connected_ == false); |
| |
| shim_l2cap_->CloseConnection(cid); |
| } |
| |
| TEST_F(ShimL2capTest, ConnectOpen) { |
| SetConnectionFuture(); |
| uint16_t cid = CreateConnection(kPsm, device_address); |
| ASSERT(cid != 0); |
| WaitConnectionFuture(); |
| |
| ASSERT(NumberOfConnections() == 1); |
| |
| hci::Address address; |
| hci::Address::FromString(device_address, address); |
| test_link_.device_with_type_ = hci::AddressWithType(address, hci::AddressType::PUBLIC_DEVICE_ADDRESS); |
| |
| l2cap::Psm psm = kPsm; |
| l2cap::Cid local_cid = kCid; |
| l2cap::Cid remote_cid = kCid2; |
| |
| std::shared_ptr<l2cap::internal::DynamicChannelImpl> impl = |
| std::make_shared<l2cap::internal::DynamicChannelImpl>(psm, local_cid, remote_cid, &test_link_, handler_); |
| |
| auto channel = std::make_unique<l2cap::DynamicChannel>(impl, handler_); |
| |
| std::promise<void> on_fail_promise; |
| auto on_fail_future = on_fail_promise.get_future(); |
| |
| auto connection_complete_future = connection_complete_promise_.get_future(); |
| handler_->Post(common::BindOnce(&TestDynamicChannelManagerImpl::SetConnectionOnOpen, |
| common::Unretained(test_l2cap_classic_module_->impl_.get()), std::move(channel), |
| std::move(on_fail_promise))); |
| connection_complete_future.wait(); |
| |
| on_fail_future.wait(); |
| |
| ASSERT(connection_connected_ == true); |
| |
| auto future = test_link_.connection_closed_promise_.get_future(); |
| shim_l2cap_->CloseConnection(cid); |
| future.wait(); |
| } |
| |
| TEST_F(ShimL2capTest, RegisterService_Success) { |
| std::promise<uint16_t> registration_promise; |
| auto registration_pending = registration_promise.get_future(); |
| |
| SetRegistrationFuture(); |
| shim_l2cap_->RegisterService( |
| kPsm, kNoUseErtm, kMtu, |
| std::bind(&bluetooth::shim::ShimL2capTest::OnConnectionComplete, this, std::placeholders::_1, |
| std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), |
| std::move(registration_promise)); |
| WaitRegistrationFuture(); |
| ASSERT_LOG(test_l2cap_classic_module_->impl_->on_registration_complete_, "Synchronization failure"); |
| ASSERT(test_l2cap_classic_module_->impl_->services_ == 1); |
| |
| l2cap::classic::DynamicChannelManager::RegistrationResult result{ |
| l2cap::classic::DynamicChannelManager::RegistrationResult::SUCCESS, |
| }; |
| auto service = std::make_unique<TestDynamicChannelService>(kPsm, &mock_, handler_); |
| |
| handler_->Post(common::BindOnce(std::move(test_l2cap_classic_module_->impl_->on_registration_complete_), result, |
| std::move(service))); |
| uint16_t psm = registration_pending.get(); |
| ASSERT(psm == kPsm); |
| } |
| |
| TEST_F(ShimL2capTest, RegisterService_Duplicate) { |
| std::promise<uint16_t> promise; |
| auto future = promise.get_future(); |
| |
| SetRegistrationFuture(); |
| shim_l2cap_->RegisterService( |
| kPsm, kNoUseErtm, kMtu, |
| std::bind(&bluetooth::shim::ShimL2capTest::OnConnectionComplete, this, std::placeholders::_1, |
| std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), |
| std::move(promise)); |
| WaitRegistrationFuture(); |
| ASSERT_LOG(test_l2cap_classic_module_->impl_->on_registration_complete_, "Synchronization failure"); |
| ASSERT(test_l2cap_classic_module_->impl_->services_ == 1); |
| |
| l2cap::classic::DynamicChannelManager::RegistrationResult result{ |
| l2cap::classic::DynamicChannelManager::RegistrationResult::FAIL_DUPLICATE_SERVICE, |
| }; |
| auto service = std::make_unique<TestDynamicChannelService>(kPsm, &mock_, handler_); |
| |
| handler_->Post(common::BindOnce(std::move(test_l2cap_classic_module_->impl_->on_registration_complete_), result, |
| std::move(service))); |
| uint16_t psm = future.get(); |
| ASSERT(psm == l2cap::kDefaultPsm); |
| } |
| |
| TEST_F(ShimL2capTest, RegisterService_Invalid) { |
| std::promise<uint16_t> promise; |
| auto future = promise.get_future(); |
| |
| SetRegistrationFuture(); |
| |
| shim_l2cap_->RegisterService( |
| kPsm, kNoUseErtm, kMtu, |
| std::bind(&bluetooth::shim::ShimL2capTest::OnConnectionComplete, this, std::placeholders::_1, |
| std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), |
| std::move(promise)); |
| |
| l2cap::classic::DynamicChannelManager::RegistrationResult result{ |
| l2cap::classic::DynamicChannelManager::RegistrationResult::FAIL_INVALID_SERVICE, |
| }; |
| auto service = std::make_unique<TestDynamicChannelService>(kPsm, &mock_, handler_); |
| WaitRegistrationFuture(); |
| |
| ASSERT_LOG(test_l2cap_classic_module_->impl_->on_registration_complete_, "Synchronization failure"); |
| handler_->Post(common::BindOnce(std::move(test_l2cap_classic_module_->impl_->on_registration_complete_), result, |
| std::move(service))); |
| uint16_t psm = future.get(); |
| ASSERT(psm == l2cap::kDefaultPsm); |
| ASSERT(test_l2cap_classic_module_->impl_->services_ == 1); |
| } |
| |
| } // namespace |
| } // namespace shim |
| } // namespace bluetooth |