Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit df98c010 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Add gd shim l2cap unit test"

parents 632d2263 897f7d55
Loading
Loading
Loading
Loading
+7 −7
Original line number Diff line number Diff line
@@ -29,20 +29,20 @@ namespace shim {

using ConnectionClosedCallback = std::function<void(uint16_t cid, int error_code)>;
using ConnectionCompleteCallback =
    std::function<void(std::string string_address, uint16_t psm, uint16_t cid, bool connected)>;
    std::function<void(std::string string_address, uint16_t psm, uint16_t cid, bool is_connected)>;
using ReadDataReadyCallback = std::function<void(uint16_t cid, std::vector<const uint8_t> data)>;

using RegisterServicePending = std::promise<uint16_t>;
using UnregisterServicePending = std::promise<void>;
using CreateConnectionPending = std::promise<uint16_t>;
using RegisterServicePromise = std::promise<uint16_t>;
using UnregisterServicePromise = std::promise<void>;
using CreateConnectionPromise = std::promise<uint16_t>;

struct IL2cap {
  virtual void RegisterService(uint16_t psm, bool use_ertm, uint16_t mtu, ConnectionCompleteCallback on_complete,
                               RegisterServicePending register_pending) = 0;
  virtual void UnregisterService(uint16_t psm, UnregisterServicePending unregister_pending) = 0;
                               RegisterServicePromise register_promise) = 0;
  virtual void UnregisterService(uint16_t psm, UnregisterServicePromise unregister_promise) = 0;

  virtual void CreateConnection(uint16_t psm, const std::string address, ConnectionCompleteCallback on_complete,
                                CreateConnectionPending create_pending) = 0;
                                CreateConnectionPromise create_promise) = 0;
  virtual void CloseConnection(uint16_t cid) = 0;

  virtual void SetReadDataReadyCallback(uint16_t cid, ReadDataReadyCallback on_data_ready) = 0;
+209 −156

File changed.

Preview size limit exceeded, changes collapsed.

+3 −3
Original line number Diff line number Diff line
@@ -30,11 +30,11 @@ namespace shim {
class L2cap : public bluetooth::Module, public bluetooth::shim::IL2cap {
 public:
  void RegisterService(uint16_t psm, bool use_ertm, uint16_t mtu, ConnectionCompleteCallback on_complete,
                       RegisterServicePending register_pending) override;
  void UnregisterService(uint16_t psm, UnregisterServicePending unregister_pending) override;
                       RegisterServicePromise register_promise) override;
  void UnregisterService(uint16_t psm, UnregisterServicePromise unregister_promise) override;

  void CreateConnection(uint16_t psm, const std::string address_string, ConnectionCompleteCallback on_complete,
                        CreateConnectionPending create_pending) override;
                        CreateConnectionPromise create_promise) override;
  void CloseConnection(uint16_t cid) override;

  void SetReadDataReadyCallback(uint16_t cid, ReadDataReadyCallback on_data_ready) override;
+244 −63
Original line number Diff line number Diff line
@@ -16,24 +16,20 @@

#include "shim/l2cap.h"

#include <algorithm>
#include <chrono>
#include <gtest/gtest.h>
#include <future>
#include <map>
#include <memory>

#include <unistd.h>

#include <gtest/gtest.h>

#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 "module.h"
#include "os/handler.h"

namespace bluetooth {
@@ -41,7 +37,13 @@ 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:
@@ -50,15 +52,32 @@ class TestDynamicChannelService : public l2cap::classic::DynamicChannelService {
      : 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_connection_open,
                      l2cap::Psm psm, l2cap::classic::DynamicChannelManager::OnConnectionOpenCallback on_open_callback,
                      l2cap::classic::DynamicChannelManager::OnConnectionFailureCallback on_fail_callback,
                      os::Handler* handler) {
    connections_++;
    if (connected_promise_ != nullptr) connected_promise_->set_value();
    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};
@@ -66,48 +85,77 @@ class TestDynamicChannelManagerImpl {
  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_connection_open,
                       l2cap::classic::DynamicChannelManager::OnConnectionOpenCallback on_open_callback,
                       os::Handler* handler) {
    services_++;
    on_registration_complete_ = std::move(on_registration_complete);
    on_connection_open_ = std::move(on_connection_open);
    on_open_callback_ = std::move(on_open_callback);

    if (registered_promise_ != nullptr) registered_promise_->set_value();
    register_promise_.set_value();
    return true;
  }
  int services_{0};

  ~TestDynamicChannelManagerImpl() {
    if (connected_promise_ != nullptr) delete connected_promise_;
    if (registered_promise_ != nullptr) delete registered_promise_;
  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>();
  }
  std::promise<void>* connected_promise_{nullptr};
  std::promise<void>* registered_promise_{nullptr};

  l2cap::classic::DynamicChannelManager::OnRegistrationCompleteCallback on_registration_complete_;
  l2cap::classic::DynamicChannelManager::OnConnectionOpenCallback on_connection_open_;
  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_connection_open,
                      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_connection_open),
    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_connection_open,
                       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_connection_open), handler);
                                 std::move(on_open_callback), handler);
  }
  TestDynamicChannelManager(TestDynamicChannelManagerImpl& impl) : impl_(impl) {}
  TestDynamicChannelManagerImpl& impl_;
@@ -137,11 +185,61 @@ void TestL2capClassicModule::Stop() {

class ShimL2capTest : public ::testing::Test {
 public:
  void OnConnectionComplete(std::string string_address, uint16_t psm, uint16_t cid, bool connected) {}
  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_);
@@ -167,94 +265,177 @@ class ShimL2capTest : public ::testing::Test {
  os::Thread& thread_ = fake_registry_.GetTestThread();
};

TEST_F(ShimL2capTest, Module) {}
TEST_F(ShimL2capTest, CreateThenDisconnectBeforeCompletion) {
  SetConnectionFuture();

TEST_F(ShimL2capTest, ConnectThenDisconnectBeforeCompletion) {
  std::promise<uint16_t> promise;
  auto future = promise.get_future();
  ASSERT(NumberOfConnections() == 0);
  uint16_t cid = CreateConnection(kPsm, device_address);
  ASSERT(cid != 0);

  test_l2cap_classic_module_->impl_->connected_promise_ = new std::promise<void>();
  auto connection_started = test_l2cap_classic_module_->impl_->connected_promise_->get_future();
  WaitConnectionFuture();
  ASSERT(NumberOfConnections() == 1);

  shim_l2cap_->CreateConnection(
      kPsm, device_address,
      std::bind(&bluetooth::shim::ShimL2capTest::OnConnectionComplete, this, std::placeholders::_1,
                std::placeholders::_2, std::placeholders::_3, std::placeholders::_4),
      std::move(promise));
  uint16_t cid = future.get();
  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();

  connection_started.wait();
    ASSERT(NumberOfConnections() == 2);
  }
}

TEST_F(ShimL2capTest, ConnectFail) {
  SetConnectionFuture();
  uint16_t cid = CreateConnection(kPsm, device_address);
  ASSERT(cid != 0);
  WaitConnectionFuture();

  ASSERT(test_l2cap_classic_module_->impl_->connections_ == 1);
  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, RegisterService_Success) {
  std::promise<uint16_t> promise;
  auto future = promise.get_future();
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_l2cap_classic_module_->impl_->registered_promise_ = new std::promise<void>();
  auto registration_started = test_l2cap_classic_module_->impl_->registered_promise_->get_future();
TEST_F(ShimL2capTest, RegisterService_Success) {
  std::promise<uint16_t> registration_promise;
  auto registration_pending = registration_promise.get_future();

  SetRegistrationFuture();
  shim_l2cap_->RegisterService(
      kPsm, false, 1000,
      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));
      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_);
  registration_started.wait();

  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();
  uint16_t psm = registration_pending.get();
  ASSERT(psm == kPsm);
  ASSERT(test_l2cap_classic_module_->impl_->services_ == 1);
}

TEST_F(ShimL2capTest, RegisterService_Duplicate) {
  std::promise<uint16_t> promise;
  auto future = promise.get_future();

  test_l2cap_classic_module_->impl_->registered_promise_ = new std::promise<void>();
  auto registration_started = test_l2cap_classic_module_->impl_->registered_promise_->get_future();

  SetRegistrationFuture();
  shim_l2cap_->RegisterService(
      kPsm, false, 1000,
      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_);
  registration_started.wait();

  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);
}

TEST_F(ShimL2capTest, RegisterService_Invalid) {
  std::promise<uint16_t> promise;
  auto future = promise.get_future();

  test_l2cap_classic_module_->impl_->registered_promise_ = new std::promise<void>();
  auto registration_started = test_l2cap_classic_module_->impl_->registered_promise_->get_future();
  SetRegistrationFuture();

  shim_l2cap_->RegisterService(
      kPsm, false, 1000,
      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));
@@ -263,7 +444,7 @@ TEST_F(ShimL2capTest, RegisterService_Invalid) {
      l2cap::classic::DynamicChannelManager::RegistrationResult::FAIL_INVALID_SERVICE,
  };
  auto service = std::make_unique<TestDynamicChannelService>(kPsm, &mock_, handler_);
  registration_started.wait();
  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,
+9 −9
Original line number Diff line number Diff line
@@ -172,8 +172,8 @@ uint16_t bluetooth::shim::legacy::L2cap::RegisterService(
  }

  LOG_DEBUG(LOG_TAG, "Registering service on psm:%hd", psm);
  RegisterServicePending register_pending;
  auto service_registered = register_pending.get_future();
  RegisterServicePromise register_promise;
  auto service_registered = register_promise.get_future();
  bool use_ertm = false;
  if (p_ertm_info != nullptr &&
      p_ertm_info->preferred_mode == L2CAP_FCR_ERTM_MODE) {
@@ -186,7 +186,7 @@ uint16_t bluetooth::shim::legacy::L2cap::RegisterService(
          &bluetooth::shim::legacy::L2cap::OnRemoteInitiatedConnectionCreated,
          this, std::placeholders::_1, std::placeholders::_2,
          std::placeholders::_3),
      std::move(register_pending));
      std::move(register_promise));

  uint16_t registered_psm = service_registered.get();
  if (registered_psm != psm) {
@@ -213,10 +213,10 @@ void bluetooth::shim::legacy::L2cap::UnregisterService(uint16_t psm) {
  }

  LOG_DEBUG(LOG_TAG, "Unregistering service on psm:%hd", psm);
  UnregisterServicePending unregister_pending;
  auto service_unregistered = unregister_pending.get_future();
  UnregisterServicePromise unregister_promise;
  auto service_unregistered = unregister_promise.get_future();
  bluetooth::shim::GetL2cap()->UnregisterService(psm,
                                                 std::move(unregister_pending));
                                                 std::move(unregister_promise));
  service_unregistered.wait();
  Classic().UnregisterPsm(psm);
}
@@ -229,8 +229,8 @@ uint16_t bluetooth::shim::legacy::L2cap::CreateConnection(
    return kInvalidConnectionInterfaceDescriptor;
  }

  CreateConnectionPending create_pending;
  auto created = create_pending.get_future();
  CreateConnectionPromise create_promise;
  auto created = create_promise.get_future();
  LOG_DEBUG(LOG_TAG, "Initiating local connection to psm:%hd address:%s", psm,
            raw_address.ToString().c_str());

@@ -240,7 +240,7 @@ uint16_t bluetooth::shim::legacy::L2cap::CreateConnection(
          &bluetooth::shim::legacy::L2cap::OnLocalInitiatedConnectionCreated,
          this, std::placeholders::_1, std::placeholders::_2,
          std::placeholders::_3, std::placeholders::_4),
      std::move(create_pending));
      std::move(create_promise));

  uint16_t cid = created.get();
  if (cid == kInvalidConnectionInterfaceDescriptor) {
Loading