/* * 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 #include #include #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 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 WaitConnectionFuture() { connected_future_ = connected_promise_.get_future(); connected_future_.wait(); } void SetRegistrationFuture() { register_promise_ = std::promise(); } void WaitRegistrationFuture() { register_future_ = register_promise_.get_future(); register_future_.wait(); } void SetConnectionOnFail(l2cap::classic::DynamicChannelManager::ConnectionResult result, std::promise promise) { std::move(on_fail_callback_).Run(result); promise.set_value(); } void SetConnectionOnOpen(std::unique_ptr channel, std::promise 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 connected_promise_; std::future connected_future_; std::promise register_promise_; std::future 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 GetDynamicChannelManager() override { ASSERT(impl_ != nullptr); return std::make_unique(*impl_); } void ListDependencies(ModuleList* list) override {} void Start() override; void Stop() override; std::unique_ptr impl_; }; void TestL2capClassicModule::Start() { impl_ = std::make_unique(); } 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 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 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(&thread_); shim_l2cap_ = static_cast(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 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 impl = std::make_shared(psm, local_cid, remote_cid, &test_link_, handler_); auto channel = std::make_unique(impl, handler_); std::promise 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 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(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 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(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 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(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