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

Commit cbb4b56b authored by Myles Watson's avatar Myles Watson Committed by Andre Eisenbach
Browse files

test_vendor: Refactor test channel



The test channel can always be enabled when using the AsyncManager.
 - Remove enabled_ and port_
 - Allow multiple connections
 - Pass file descriptors as parameters instead of private variables
 - Add a clean up function to the Test Channel
 - Add a static class in bt_vendor.cc and always EXPORT the entry point
 - Move the vendor callbacks and the global pointer to the vendor
   manager object from vendor_manager to bt_vendor

Change-Id: I3e0dee846eb89f434893603a705c2b13219272be
Signed-off-by: default avatarMyles Watson <mylesgw@google.com>
Signed-off-by: default avatarJorge E. Moreira <jemoreira@google.com>
parent bec7d8b8
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -56,6 +56,8 @@ LOCAL_CFLAGS += \
  -UNDEBUG \
  -DLOG_NDEBUG=1

LOCAL_CFLAGS += -DEXPORT_SYMBOL="__attribute__((visibility(\"default\")))"

include $(BUILD_SHARED_LIBRARY)

# test-vendor unit tests for host
+14 −25
Original line number Diff line number Diff line
@@ -30,45 +30,34 @@ namespace test_vendor_lib {
// HciTransport for the test channel.
class TestChannelTransport {
 public:
  TestChannelTransport(bool enabled, int port);
  TestChannelTransport() {}

  ~TestChannelTransport() = default;
  ~TestChannelTransport() {}

  // Waits for a connection request from the test channel program and
  // allocates the file descriptor to watch for run-time parameters at. This
  // file descriptor gets stored in |fd_|.
  bool SetUp();

  int GetFd();
  // Opens a port and returns the file descriptor for the socket.
  // Returns -1 on an error.
  int SetUp(int port);

  // Because it imposes a different flow of work, the test channel must be
  // actively enabled to be used. |enabled_| is set by the vendor manager.
  bool IsEnabled();
  // Closes the port (if succesfully opened in SetUp).
  void CleanUp();

  // Turns the test channel off for use in circumstances where an error occurs
  // and leaving the channel on would crash Bluetooth (e.g. if the test channel
  // is unable to bind to its socket, Bluetooth should still start without the
  // channel enabled).
  void Disable();
  // Waits for a connection request from the test channel program and
  // returns the file descriptor to watch for run-time parameters.
  // Returns -1 on an error.
  int Accept(int listen_fd);

  // Sets the callback that fires when data is read in
  // |OnFileCanReadWithoutBlocking|.
  // Sets the callback that fires when data is read in WatchFd().
  void RegisterCommandHandler(
      const std::function<void(const std::string&, const vector<std::string>&)>&
          callback);

  void OnFileCanReadWithoutBlocking(int fd);
  void OnCommandReady(int fd, std::function<void(void)> unwatch);

 private:
  std::function<void(const std::string&, const vector<std::string>&)>
      command_handler_;

  // File descriptor to watch for test hook data.
  std::unique_ptr<base::ScopedFD> fd_;

  // TODO(dennischeng): Get port and enabled flag from a config file.
  bool enabled_;
  int port_;
  int listen_fd_ = -1;

  TestChannelTransport(const TestChannelTransport& cmdPckt) = delete;
  TestChannelTransport& operator=(const TestChannelTransport& cmdPckt) = delete;
+12 −26
Original line number Diff line number Diff line
@@ -32,23 +32,20 @@ namespace test_vendor_lib {
// Contains the three core objects that make up the test vendor library: the
// HciTransport for communication, the HciHandler for processing commands, and
// the Controller for actual command implementations. The VendorManager shall
// operate as a global singleton and be used in bt_vendor.cc to perform vendor
// specific operations, via |vendor_callbacks_|, and to provide access to the
// test controller by setting up a message loop (on another thread) that the HCI
// will talk to and controller methods will execute on.
// be used in bt_vendor.cc to provide access to the test controller by setting
// up a message loop (on another thread) that the HCI will talk to and
// controller methods will execute on.
class VendorManager {
 public:
  // Functions that operate on the global manager instance. Initialize()
  // is called by the vendor library's TestVendorInitialize() function to create
  // the global manager and must be called before Get() and CleanUp().
  // CleanUp() should be called when a call to TestVendorCleanUp() is made
  // since the global manager should live throughout the entire time the test
  // vendor library is in use.
  static void CleanUp();
  VendorManager();

  ~VendorManager() = default;

  static VendorManager* Get();
  void CleanUp();

  static void Initialize();
  // Initializes the controller and sets up the test channel to wait for
  // connections.
  bool Initialize();

  void CloseHciFd();

@@ -60,17 +57,9 @@ class VendorManager {
  // the vendor library from the HCI in TestVendorInit().
  void SetVendorCallbacks(const bt_vendor_callbacks_t& callbacks);

  // Returns true if |thread_| is able to be started and the
  // StartingWatchingOnThread() task has been posted to the task runner.
  bool Run();

 private:
  VendorManager();

  ~VendorManager() = default;

  // Starts watching for incoming data from the HCI and the test hook.
  void StartWatchingOnThread();
  // Set up a test channel on _port_
  void SetUpTestChannel(int port);

  // Creates the HCI's communication channel and overrides IO callbacks to
  // receive and send packets.
@@ -86,9 +75,6 @@ class VendorManager {
  // Configuration callbacks provided by the HCI for use in TestVendorOp().
  bt_vendor_callbacks_t vendor_callbacks_;

  // True if the underlying message loop (in |thread_|) is running.
  bool running_;

  // The object that manages asynchronous tasks such as watching a file
  // descriptor or doing something in the future
  AsyncManager async_manager_;
+109 −101
Original line number Diff line number Diff line
@@ -16,40 +16,38 @@

#define LOG_TAG "bt_vendor"

#include "vendor_manager.h"
#include <unistd.h>
#include <memory>

#include "base/logging.h"

extern "C" {
#include "osi/include/log.h"

#include <unistd.h>
}  // extern "C"
#include "vendor_manager.h"

namespace test_vendor_lib {

class BtVendor {
 public:
  // Initializes vendor manager for test controller. |p_cb| are the callbacks to
  // be in TestVendorOp(). |local_bdaddr| points to the address of the Bluetooth
  // device. Returns 0 on success, -1 on error.
static int TestVendorInitialize(const bt_vendor_callbacks_t* p_cb,
  static int Initialize(const bt_vendor_callbacks_t* p_cb,
                        unsigned char* /* local_bdaddr */) {
    LOG_INFO(LOG_TAG, "Initializing test controller.");
    CHECK(p_cb);

  VendorManager::Initialize();
  VendorManager* manager = VendorManager::Get();
  manager->SetVendorCallbacks(*(const_cast<bt_vendor_callbacks_t*>(p_cb)));
  return manager->Run() ? 0 : -1;
    vendor_callbacks_ = *p_cb;

    vendor_manager_.reset(new VendorManager());
    return vendor_manager_->Initialize() ? 0 : 1;
  }

// Vendor specific operations. |opcode| is the opcode for Bluedroid's vendor op
// definitions. |param| points to operation specific arguments. Return value is
// dependent on the operation invoked, or -1 on error.
static int TestVendorOp(bt_vendor_opcode_t opcode, void* param) {
  // Vendor specific operations. |opcode| is the opcode for Bluedroid's vendor
  // op definitions. |param| points to operation specific arguments. Return
  // value is dependent on the operation invoked, or -1 on error.
  static int Op(bt_vendor_opcode_t opcode, void* param) {
    LOG_INFO(LOG_TAG, "Opcode received in vendor library: %d", opcode);

  VendorManager* manager = VendorManager::Get();
  CHECK(manager);
    CHECK(vendor_manager_);

    switch (opcode) {
      case BT_VND_OP_POWER_CTRL: {
@@ -66,7 +64,7 @@ static int TestVendorOp(bt_vendor_opcode_t opcode, void* param) {
      case BT_VND_OP_USERIAL_OPEN: {
        LOG_INFO(LOG_TAG, "Doing op: BT_VND_OP_USERIAL_OPEN");
        int* fd_list = static_cast<int*>(param);
      fd_list[0] = manager->GetHciFd();
        fd_list[0] = vendor_manager_->GetHciFd();
        LOG_INFO(LOG_TAG, "Setting HCI's fd to: %d", fd_list[0]);
        return 1;
      }
@@ -74,18 +72,19 @@ static int TestVendorOp(bt_vendor_opcode_t opcode, void* param) {
      // Close the HCI's file descriptor.
      case BT_VND_OP_USERIAL_CLOSE:
        LOG_INFO(LOG_TAG, "Doing op: BT_VND_OP_USERIAL_CLOSE");
      LOG_INFO(LOG_TAG, "Closing HCI's fd (fd: %d)", manager->GetHciFd());
      manager->CloseHciFd();
        LOG_INFO(
            LOG_TAG, "Closing HCI's fd (fd: %d)", vendor_manager_->GetHciFd());
        vendor_manager_->CloseHciFd();
        return 1;

      case BT_VND_OP_FW_CFG:
        LOG_INFO(LOG_TAG, "Unsupported op: BT_VND_OP_FW_CFG");
      manager->GetVendorCallbacks().fwcfg_cb(BT_VND_OP_RESULT_SUCCESS);
        vendor_callbacks_.fwcfg_cb(BT_VND_OP_RESULT_SUCCESS);
        return -1;

      case BT_VND_OP_SCO_CFG:
        LOG_INFO(LOG_TAG, "Unsupported op: BT_VND_OP_SCO_CFG");
      manager->GetVendorCallbacks().scocfg_cb(BT_VND_OP_RESULT_SUCCESS);
        vendor_callbacks_.scocfg_cb(BT_VND_OP_RESULT_SUCCESS);
        return -1;

      case BT_VND_OP_GET_LPM_IDLE_TIMEOUT:
@@ -95,7 +94,7 @@ static int TestVendorOp(bt_vendor_opcode_t opcode, void* param) {

      case BT_VND_OP_LPM_SET_MODE:
        LOG_INFO(LOG_TAG, "Unsupported op: BT_VND_OP_LPM_SET_MODE");
      manager->GetVendorCallbacks().lpm_cb(BT_VND_OP_RESULT_SUCCESS);
        vendor_callbacks_.lpm_cb(BT_VND_OP_RESULT_SUCCESS);
        return -1;

      case BT_VND_OP_LPM_WAKE_SET_STATE:
@@ -108,7 +107,7 @@ static int TestVendorOp(bt_vendor_opcode_t opcode, void* param) {

      case BT_VND_OP_EPILOG:
        LOG_INFO(LOG_TAG, "Unsupported op: BT_VND_OP_EPILOG");
      manager->GetVendorCallbacks().epilog_cb(BT_VND_OP_RESULT_SUCCESS);
        vendor_callbacks_.epilog_cb(BT_VND_OP_RESULT_SUCCESS);
        return -1;

      default:
@@ -119,19 +118,28 @@ static int TestVendorOp(bt_vendor_opcode_t opcode, void* param) {
  }

  // Closes the vendor interface and cleans up the global vendor manager object.
static void TestVendorCleanUp(void) {
  static void CleanUp(void) {
    LOG_INFO(LOG_TAG, "Cleaning up vendor library.");
  VendorManager::CleanUp();
    CHECK(vendor_manager_);
    vendor_manager_->CleanUp();
    vendor_manager_.reset();
  }

 private:
  static std::unique_ptr<VendorManager> vendor_manager_;
  static bt_vendor_callbacks_t vendor_callbacks_;
};

// Definition of static class members
std::unique_ptr<VendorManager> BtVendor::vendor_manager_;
bt_vendor_callbacks_t BtVendor::vendor_callbacks_;

}  // namespace test_vendor_lib

// Entry point of DLib.
#ifdef BLUETOOTH_USE_TEST_AS_VENDOR
EXPORT_SYMBOL
#endif
const bt_vendor_interface_t BLUETOOTH_VENDOR_LIB_INTERFACE = {
    sizeof(bt_vendor_interface_t),
    test_vendor_lib::TestVendorInitialize,
    test_vendor_lib::TestVendorOp,
    test_vendor_lib::TestVendorCleanUp};
    test_vendor_lib::BtVendor::Initialize,
    test_vendor_lib::BtVendor::Op,
    test_vendor_lib::BtVendor::CleanUp};
+53 −45
Original line number Diff line number Diff line
@@ -30,68 +30,78 @@ extern "C" {

namespace test_vendor_lib {

TestChannelTransport::TestChannelTransport(bool enabled, int port)
    : enabled_(enabled), port_(port) {}

bool TestChannelTransport::SetUp() {
  CHECK(enabled_);

  struct sockaddr_in listen_address, test_channel_address;
int TestChannelTransport::SetUp(int port) {
  struct sockaddr_in listen_address;
  socklen_t sockaddr_in_size = sizeof(struct sockaddr_in);
  int listen_fd = -1;
  int accept_fd = -1;
  memset(&listen_address, 0, sockaddr_in_size);
  memset(&test_channel_address, 0, sockaddr_in_size);

  if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
  OSI_NO_INTR(listen_fd_ = socket(AF_INET, SOCK_STREAM, 0));
  if (listen_fd_ < 0) {
    LOG_INFO(LOG_TAG, "Error creating socket for test channel.");
    return false;
    return -1;
  }

  LOG_INFO(LOG_TAG, "port: %d", port_);
  LOG_INFO(LOG_TAG, "port: %d", port);
  listen_address.sin_family = AF_INET;
  listen_address.sin_port = htons(port_);
  listen_address.sin_port = htons(port);
  listen_address.sin_addr.s_addr = htonl(INADDR_ANY);

  if (bind(listen_fd,
  if (bind(listen_fd_,
           reinterpret_cast<sockaddr*>(&listen_address),
           sockaddr_in_size) < 0) {
    LOG_INFO(LOG_TAG, "Error binding test channel listener socket to address.");
    close(listen_fd);
    return false;
    close(listen_fd_);
    return -1;
  }

  if (listen(listen_fd, 1) < 0) {
  if (listen(listen_fd_, 1) < 0) {
    LOG_INFO(LOG_TAG, "Error listening for test channel.");
    close(listen_fd);
    return false;
    close(listen_fd_);
    return -1;
  }

  if ((accept_fd = accept(listen_fd,
                          reinterpret_cast<sockaddr*>(&test_channel_address),
                          &sockaddr_in_size)) < 0) {
    LOG_INFO(LOG_TAG, "Error accepting test channel connection.");
    close(listen_fd);
    return false;
  return listen_fd_;
}

  fd_.reset(new base::ScopedFD(accept_fd));
  return GetFd() >= 0;
void TestChannelTransport::CleanUp() {
  if (listen_fd_ == -1) {
    return;
  }

int TestChannelTransport::GetFd() {
  return fd_->get();
  if (close(listen_fd_)) {
    LOG_ERROR(LOG_TAG, "Error closing listen_fd_.");
  }
  listen_fd_ = -1;
}

bool TestChannelTransport::IsEnabled() {
  return enabled_;
int TestChannelTransport::Accept(int listen_fd_) {
  int accept_fd = -1;
  struct sockaddr_in test_channel_address;
  socklen_t sockaddr_in_size = sizeof(struct sockaddr_in);
  memset(&test_channel_address, 0, sockaddr_in_size);

  OSI_NO_INTR(accept_fd =
                  accept(listen_fd_,
                         reinterpret_cast<sockaddr*>(&test_channel_address),
                         &sockaddr_in_size));
  if (accept_fd < 0) {
    LOG_INFO(LOG_TAG,
             "Error accepting test channel connection errno=%d (%s).",
             errno,
             strerror(errno));

    if (errno != EAGAIN && errno != EWOULDBLOCK) {
      LOG_ERROR(LOG_TAG, "Closing listen_fd_ (won't try again).");
      close(listen_fd_);
      return -1;
    }
  }

// base::MessageLoopForIO::Watcher overrides:
void TestChannelTransport::OnFileCanReadWithoutBlocking(int fd) {
  CHECK(fd == GetFd());
  LOG_INFO(LOG_TAG, "accept_fd = %d.", accept_fd);

  LOG_INFO(LOG_TAG, "Event ready in TestChannelTransport on fd: %d", fd);
  return accept_fd;
}

void TestChannelTransport::OnCommandReady(int fd,
                                          std::function<void(void)> unwatch) {
  uint8_t command_name_size = 0;
  read(fd, &command_name_size, 1);
  vector<uint8_t> command_name_raw;
@@ -101,8 +111,10 @@ void TestChannelTransport::OnFileCanReadWithoutBlocking(int fd) {
  LOG_INFO(
      LOG_TAG, "Received command from test channel: %s", command_name.data());

  if (command_name == "CLOSE_TEST_CHANNEL") {
    fd_.reset(nullptr);
  if (command_name == "CLOSE_TEST_CHANNEL" || command_name == "") {
    LOG_INFO(LOG_TAG, "Test channel closed");
    unwatch();
    close(fd);
    return;
  }

@@ -131,8 +143,4 @@ void TestChannelTransport::RegisterCommandHandler(
  command_handler_ = callback;
}

void TestChannelTransport::Disable() {
  enabled_ = false;
}

}  // namespace test_vendor_lib {
Loading