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: Myles Watson <mylesgw@google.com>
Signed-off-by: Jorge E. Moreira <jemoreira@google.com>
-UNDEBUG \
-DLOG_NDEBUG=1
+LOCAL_CFLAGS += -DEXPORT_SYMBOL="__attribute__((visibility(\"default\")))"
+
include $(BUILD_SHARED_LIBRARY)
# test-vendor unit tests for host
// 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;
// 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();
// 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.
// 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_;
#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 {
-// 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,
- 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 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) {
- LOG_INFO(LOG_TAG, "Opcode received in vendor library: %d", opcode);
-
- VendorManager* manager = VendorManager::Get();
- CHECK(manager);
-
- switch (opcode) {
- case BT_VND_OP_POWER_CTRL: {
- LOG_INFO(LOG_TAG, "Doing op: BT_VND_OP_POWER_CTRL");
- int* state = static_cast<int*>(param);
- if (*state == BT_VND_PWR_OFF)
- LOG_INFO(LOG_TAG, "Turning Bluetooth off.");
- else if (*state == BT_VND_PWR_ON)
- LOG_INFO(LOG_TAG, "Turning Bluetooth on.");
- return 0;
- }
+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 Initialize(const bt_vendor_callbacks_t* p_cb,
+ unsigned char* /* local_bdaddr */) {
+ LOG_INFO(LOG_TAG, "Initializing test controller.");
+ CHECK(p_cb);
+
+ vendor_callbacks_ = *p_cb;
+
+ vendor_manager_.reset(new VendorManager());
+ return vendor_manager_->Initialize() ? 0 : 1;
+ }
- // Give the HCI its fd to communicate with the HciTransport.
- 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();
- LOG_INFO(LOG_TAG, "Setting HCI's fd to: %d", fd_list[0]);
- return 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 Op(bt_vendor_opcode_t opcode, void* param) {
+ LOG_INFO(LOG_TAG, "Opcode received in vendor library: %d", opcode);
+
+ CHECK(vendor_manager_);
+
+ switch (opcode) {
+ case BT_VND_OP_POWER_CTRL: {
+ LOG_INFO(LOG_TAG, "Doing op: BT_VND_OP_POWER_CTRL");
+ int* state = static_cast<int*>(param);
+ if (*state == BT_VND_PWR_OFF)
+ LOG_INFO(LOG_TAG, "Turning Bluetooth off.");
+ else if (*state == BT_VND_PWR_ON)
+ LOG_INFO(LOG_TAG, "Turning Bluetooth on.");
+ return 0;
+ }
+
+ // Give the HCI its fd to communicate with the HciTransport.
+ 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] = vendor_manager_->GetHciFd();
+ LOG_INFO(LOG_TAG, "Setting HCI's fd to: %d", fd_list[0]);
+ return 1;
+ }
+
+ // 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)", 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");
+ 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");
+ vendor_callbacks_.scocfg_cb(BT_VND_OP_RESULT_SUCCESS);
+ return -1;
+
+ case BT_VND_OP_GET_LPM_IDLE_TIMEOUT:
+ LOG_INFO(LOG_TAG, "Doing op: BT_VND_OP_SCO_CFG");
+ *((uint32_t*)param) = 1000;
+ return 0;
+
+ case BT_VND_OP_LPM_SET_MODE:
+ LOG_INFO(LOG_TAG, "Unsupported op: BT_VND_OP_LPM_SET_MODE");
+ vendor_callbacks_.lpm_cb(BT_VND_OP_RESULT_SUCCESS);
+ return -1;
+
+ case BT_VND_OP_LPM_WAKE_SET_STATE:
+ LOG_INFO(LOG_TAG, "Unsupported op: BT_VND_OP_LPM_WAKE_SET_STATE");
+ return -1;
+
+ case BT_VND_OP_SET_AUDIO_STATE:
+ LOG_INFO(LOG_TAG, "Unsupported op: BT_VND_OP_SET_AUDIO_STATE");
+ return -1;
+
+ case BT_VND_OP_EPILOG:
+ LOG_INFO(LOG_TAG, "Unsupported op: BT_VND_OP_EPILOG");
+ vendor_callbacks_.epilog_cb(BT_VND_OP_RESULT_SUCCESS);
+ return -1;
+
+ default:
+ LOG_INFO(LOG_TAG, "Op not recognized.");
+ return -1;
}
+ return 0;
+ }
- // 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();
- 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);
- 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);
- return -1;
-
- case BT_VND_OP_GET_LPM_IDLE_TIMEOUT:
- LOG_INFO(LOG_TAG, "Doing op: BT_VND_OP_SCO_CFG");
- *((uint32_t*)param) = 1000;
- return 0;
-
- 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);
- return -1;
-
- case BT_VND_OP_LPM_WAKE_SET_STATE:
- LOG_INFO(LOG_TAG, "Unsupported op: BT_VND_OP_LPM_WAKE_SET_STATE");
- return -1;
-
- case BT_VND_OP_SET_AUDIO_STATE:
- LOG_INFO(LOG_TAG, "Unsupported op: BT_VND_OP_SET_AUDIO_STATE");
- return -1;
-
- case BT_VND_OP_EPILOG:
- LOG_INFO(LOG_TAG, "Unsupported op: BT_VND_OP_EPILOG");
- manager->GetVendorCallbacks().epilog_cb(BT_VND_OP_RESULT_SUCCESS);
- return -1;
-
- default:
- LOG_INFO(LOG_TAG, "Op not recognized.");
- return -1;
+ // Closes the vendor interface and cleans up the global vendor manager object.
+ static void CleanUp(void) {
+ LOG_INFO(LOG_TAG, "Cleaning up vendor library.");
+ CHECK(vendor_manager_);
+ vendor_manager_->CleanUp();
+ vendor_manager_.reset();
}
- return 0;
-}
-// Closes the vendor interface and cleans up the global vendor manager object.
-static void TestVendorCleanUp(void) {
- LOG_INFO(LOG_TAG, "Cleaning up vendor library.");
- VendorManager::CleanUp();
-}
+ 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};
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;
}
+ return listen_fd_;
+}
- 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;
+void TestChannelTransport::CleanUp() {
+ if (listen_fd_ == -1) {
+ return;
}
-
- fd_.reset(new base::ScopedFD(accept_fd));
- return GetFd() >= 0;
+ if (close(listen_fd_)) {
+ LOG_ERROR(LOG_TAG, "Error closing listen_fd_.");
+ }
+ listen_fd_ = -1;
}
-int TestChannelTransport::GetFd() {
- return fd_->get();
-}
+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);
-bool TestChannelTransport::IsEnabled() {
- return enabled_;
-}
+ 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;
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;
}
command_handler_ = callback;
}
-void TestChannelTransport::Disable() {
- enabled_ = false;
-}
-
} // namespace test_vendor_lib {
#include "base/logging.h"
-extern "C" {
#include "osi/include/log.h"
-} // extern "C"
namespace test_vendor_lib {
VendorManager* g_manager = nullptr;
-// static
void VendorManager::CleanUp() {
- delete g_manager;
- g_manager = nullptr;
+ test_channel_transport_.CleanUp();
}
-// static
-VendorManager* VendorManager::Get() {
- // Initialize should have been called already.
- CHECK(g_manager);
- return g_manager;
-}
-
-// static
-void VendorManager::Initialize() {
- CHECK(!g_manager);
- g_manager = new VendorManager();
-}
-
-VendorManager::VendorManager()
- : test_channel_transport_(true, 6111), running_(false) {}
-
-bool VendorManager::Run() {
- CHECK(!running_);
-
+bool VendorManager::Initialize() {
if (!transport_.SetUp()) {
LOG_ERROR(LOG_TAG, "Error setting up transport object.");
return false;
}
- if (test_channel_transport_.IsEnabled()) {
- LOG_INFO(LOG_TAG, "Test channel is enabled.");
+ controller_.RegisterHandlersWithHciTransport(transport_);
- if (test_channel_transport_.SetUp()) {
- controller_.RegisterHandlersWithTestChannelTransport(
- test_channel_transport_);
- } else {
- LOG_ERROR(LOG_TAG,
- "Error setting up test channel object, continuing without it.");
- test_channel_transport_.Disable();
- }
- } else {
- LOG_INFO(LOG_TAG, "Test channel is disabled.");
- }
+ controller_.RegisterHandlersWithTestChannelTransport(test_channel_transport_);
- controller_.RegisterHandlersWithHciTransport(transport_);
- // TODO(dennischeng): Register PostDelayedEventResponse instead.
- controller_.RegisterDelayedEventChannel([this](
- std::unique_ptr<EventPacket> event, std::chrono::milliseconds delay) {
- transport_.PostDelayedEventResponse(*event, delay);
+ controller_.RegisterEventChannel([this](std::unique_ptr<EventPacket> event) {
+ transport_.PostEventResponse(*event);
});
transport_.RegisterEventScheduler(
async_manager_.ExecAsyncPeriodically(delay, period, task);
});
- running_ = true;
- StartWatchingOnThread();
-
- return true;
-}
-
-void VendorManager::StartWatchingOnThread() {
- CHECK(running_);
-
if (async_manager_.WatchFdForNonBlockingReads(
transport_.GetVendorFd(), [this](int fd) {
transport_.OnFileCanReadWithoutBlocking(fd);
}) != 0) {
LOG_ERROR(LOG_TAG, "Error watching vendor fd.");
- return;
+ return true;
}
- if (test_channel_transport_.IsEnabled())
- if (async_manager_.WatchFdForNonBlockingReads(
- test_channel_transport_.GetFd(), [this](int fd) {
- test_channel_transport_.OnFileCanReadWithoutBlocking(fd);
- }) != 0) {
- LOG_ERROR(LOG_TAG, "Error watching test channel fd.");
- }
-}
+ SetUpTestChannel(6111);
-void VendorManager::SetVendorCallbacks(const bt_vendor_callbacks_t& callbacks) {
- vendor_callbacks_ = callbacks;
+ return true;
}
-const bt_vendor_callbacks_t& VendorManager::GetVendorCallbacks() const {
- return vendor_callbacks_;
+VendorManager::VendorManager() : test_channel_transport_() {}
+
+void VendorManager::SetUpTestChannel(int port) {
+ int socket_fd = test_channel_transport_.SetUp(port);
+
+ if (socket_fd == -1) {
+ LOG_ERROR(LOG_TAG, "Test channel SetUp(%d) failed.", port);
+ return;
+ }
+
+ LOG_INFO(LOG_TAG, "Test channel SetUp() successful");
+ async_manager_.WatchFdForNonBlockingReads(socket_fd, [this](int socket_fd) {
+ int conn_fd = test_channel_transport_.Accept(socket_fd);
+ if (conn_fd < 0) {
+ LOG_ERROR(LOG_TAG, "Error watching test channel fd.");
+ return;
+ }
+ LOG_INFO(LOG_TAG, "Test channel connection accepted.");
+ async_manager_.WatchFdForNonBlockingReads(conn_fd, [this](int conn_fd) {
+ test_channel_transport_.OnCommandReady(conn_fd, [this, conn_fd]() {
+ async_manager_.StopWatchingFileDescriptor(conn_fd);
+ });
+ });
+ });
}
void VendorManager::CloseHciFd() {