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

Commit f795c73a authored by Myles Watson's avatar Myles Watson Committed by Hansong Zhang
Browse files

RootCanal: Desktop simulation envrionment

Test: nativetest64/root-canal/root-canal [TEST_PORT] [HCI_PORT] [LINK_PORT]
      python scripts/test_channel.py

Change-Id: I6e57981182c392366d7d97249d837694b49dfa4e
parent c139279b
Loading
Loading
Loading
Loading
+32 −0
Original line number Diff line number Diff line
@@ -107,3 +107,35 @@ cc_test_host {
        "-DLOG_NDEBUG=1",
    ],
}

// Linux RootCanal Executable
// ========================================================
cc_test_host {
    name: "root-canal",
    defaults: [
        "libchrome_support_defaults",
    ],
    srcs: [
        "desktop/root_canal_main.cc",
        "desktop/test_environment.cc",
    ],
    header_libs: [
        "libbluetooth_headers",
    ],
    local_include_dirs: [
        "include",
    ],
    include_dirs: [
        "packages/modules/Bluetooth/system",
        "packages/modules/Bluetooth/system/utils/include",
        "packages/modules/Bluetooth/system/hci/include",
        "packages/modules/Bluetooth/system/stack/include",
    ],
    shared_libs: [
        "liblog",
    ],
    static_libs: [
        "libbt-rootcanal-types",
        "libbt-rootcanal",
    ],
}
+69 −0
Original line number Diff line number Diff line
//
// Copyright 2017 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.
//

#define LOG_TAG "root_canal"

#include "test_environment.h"

#include <base/logging.h>
#include <utils/Log.h>
#include <future>

#include "hci_internals.h"

using ::android::bluetooth::root_canal::TestEnvironment;

constexpr uint16_t kTestPort = 6401;
constexpr uint16_t kHciServerPort = 6402;
constexpr uint16_t kLinkServerPort = 6403;

int main(int argc, char** argv) {
  ALOGI("main");
  uint16_t test_port = kTestPort;
  uint16_t hci_server_port = kHciServerPort;
  uint16_t link_server_port = kLinkServerPort;

  for (int arg = 0; arg < argc; arg++) {
    int port = atoi(argv[arg]);
    ALOGI("%d: %s (%d)", arg, argv[arg], port);
    if (port < 0 || port > 0xffff) {
      ALOGW("%s out of range", argv[arg]);
    } else {
      switch (arg) {
        case 0:  // executable name
          break;
        case 1:
          test_port = port;
          break;
        case 2:
          hci_server_port = port;
          break;
        case 3:
          link_server_port = port;
          break;
        default:
          ALOGW("Ignored option %s", argv[arg]);
      }
    }
  }

  TestEnvironment root_canal(test_port, hci_server_port, link_server_port);
  std::promise<void> barrier;
  std::future<void> barrier_future = barrier.get_future();
  root_canal.initialize(std::move(barrier));
  barrier_future.wait();
  root_canal.close();
}
+178 −0
Original line number Diff line number Diff line
//
// Copyright 2017 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.
//

#define LOG_TAG "root_canal"

#include "test_environment.h"

#include <base/logging.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
#include <utils/Log.h>

#include "hci_internals.h"

namespace android {
namespace bluetooth {
namespace root_canal {

using test_vendor_lib::AsyncTaskId;
using test_vendor_lib::DualModeController;
using test_vendor_lib::TaskCallback;

void TestEnvironment::initialize(std::promise<void> barrier) {
  ALOGI("%s", __func__);

  barrier_ = std::move(barrier);

  test_channel_transport_.RegisterCommandHandler([this](const std::string& name, const std::vector<std::string>& args) {
    async_manager_.ExecAsync(std::chrono::milliseconds(0),
                             [this, name, args]() { test_channel_.HandleCommand(name, args); });
  });

  test_model_.Reset();

  SetUpTestChannel();
  SetUpHciServer([this](int fd) { test_model_.IncomingHciConnection(fd); });
  SetUpLinkLayerServer([this](int fd) { test_model_.IncomingLinkLayerConnection(fd); });

  ALOGI("%s: Finished", __func__);
}

void TestEnvironment::close() {
  ALOGI("%s", __func__);
}

void TestEnvironment::SetUpHciServer(const std::function<void(int)>& connection_callback) {
  int socket_fd = remote_hci_transport_.SetUp(hci_server_port_);

  test_channel_.RegisterSendResponse(
      [](const std::string& response) { ALOGI("No HCI Response channel: %s", response.c_str()); });

  if (socket_fd == -1) {
    ALOGE("Remote HCI channel SetUp(%d) failed.", hci_server_port_);
    return;
  }

  async_manager_.WatchFdForNonBlockingReads(socket_fd, [this, connection_callback](int socket_fd) {
    int conn_fd = remote_hci_transport_.Accept(socket_fd);
    if (conn_fd < 0) {
      ALOGE("Error watching remote HCI channel fd.");
      return;
    }
    int flags = fcntl(conn_fd, F_GETFL, NULL);
    int ret;
    ret = fcntl(conn_fd, F_SETFL, flags | O_NONBLOCK);
    CHECK(ret != -1) << "Error setting O_NONBLOCK " << strerror(errno);

    connection_callback(conn_fd);
  });
}

void TestEnvironment::SetUpLinkLayerServer(const std::function<void(int)>& connection_callback) {
  int socket_fd = remote_link_layer_transport_.SetUp(link_server_port_);

  test_channel_.RegisterSendResponse(
      [](const std::string& response) { ALOGI("No LinkLayer Response channel: %s", response.c_str()); });

  if (socket_fd == -1) {
    ALOGE("Remote LinkLayer channel SetUp(%d) failed.", link_server_port_);
    return;
  }

  async_manager_.WatchFdForNonBlockingReads(socket_fd, [this, connection_callback](int socket_fd) {
    int conn_fd = remote_link_layer_transport_.Accept(socket_fd);
    if (conn_fd < 0) {
      ALOGE("Error watching remote LinkLayer channel fd.");
      return;
    }
    int flags = fcntl(conn_fd, F_GETFL, NULL);
    int ret = fcntl(conn_fd, F_SETFL, flags | O_NONBLOCK);
    CHECK(ret != -1) << "Error setting O_NONBLOCK " << strerror(errno);

    connection_callback(conn_fd);
  });
}

int TestEnvironment::ConnectToRemoteServer(const std::string& server, int port) {
  int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
  if (socket_fd < 1) {
    ALOGI("socket() call failed: %s", strerror(errno));
    return -1;
  }

  struct hostent* host;
  host = gethostbyname(server.c_str());
  if (host == NULL) {
    ALOGI("gethostbyname() failed for %s: %s", server.c_str(), strerror(errno));
    return -1;
  }

  struct sockaddr_in serv_addr;
  memset((void*)&serv_addr, 0, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_addr.s_addr = INADDR_ANY;
  serv_addr.sin_port = htons(port);

  int result = connect(socket_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
  if (result < 0) {
    ALOGI("connect() failed for %s@%d: %s", server.c_str(), port, strerror(errno));
    return -1;
  }

  int flags = fcntl(socket_fd, F_GETFL, NULL);
  int ret = fcntl(socket_fd, F_SETFL, flags | O_NONBLOCK);
  CHECK(ret != -1) << "Error setting O_NONBLOCK " << strerror(errno);

  return socket_fd;
}

void TestEnvironment::SetUpTestChannel() {
  int socket_fd = test_channel_transport_.SetUp(test_port_);

  test_channel_.RegisterSendResponse(
      [](const std::string& response) { ALOGI("No test channel: %s", response.c_str()); });

  if (socket_fd == -1) {
    ALOGE("Test channel SetUp(%d) failed.", test_port_);
    return;
  }

  ALOGI("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) {
      ALOGE("Error watching test channel fd.");
      barrier_.set_value();
      return;
    }
    ALOGI("Test channel connection accepted.");
    test_channel_.RegisterSendResponse(
        [this, conn_fd](const std::string& response) { test_channel_transport_.SendResponse(conn_fd, response); });

    async_manager_.WatchFdForNonBlockingReads(conn_fd, [this](int conn_fd) {
      test_channel_transport_.OnCommandReady(conn_fd, [this, conn_fd]() {
        async_manager_.StopWatchingFileDescriptor(conn_fd);
        barrier_.set_value();
      });
    });
  });
}

}  // namespace root_canal
}  // namespace bluetooth
}  // namespace android
+78 −0
Original line number Diff line number Diff line
//
// Copyright 2017 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.
//

#pragma once

#include <future>

#include "model/controller/dual_mode_controller.h"
#include "model/setup/async_manager.h"
#include "model/setup/test_channel_transport.h"
#include "model/setup/test_command_handler.h"
#include "model/setup/test_model.h"

namespace android {
namespace bluetooth {
namespace root_canal {

class TestEnvironment {
 public:
  TestEnvironment(uint16_t test_port, uint16_t hci_server_port, uint16_t link_server_port)
      : test_port_(test_port), hci_server_port_(hci_server_port), link_server_port_(link_server_port) {}

  void initialize(std::promise<void> barrier);

  void close();

 private:
  uint16_t test_port_;
  uint16_t hci_server_port_;
  uint16_t link_server_port_;
  std::promise<void> barrier_;

  test_vendor_lib::AsyncManager async_manager_;

  void SetUpTestChannel();
  void SetUpHciServer(const std::function<void(int)>& on_connect);
  void SetUpLinkLayerServer(const std::function<void(int)>& on_connect);
  int ConnectToRemoteServer(const std::string& server, int port);

  std::shared_ptr<test_vendor_lib::DualModeController> controller_;

  test_vendor_lib::TestChannelTransport test_channel_transport_;
  test_vendor_lib::TestChannelTransport remote_hci_transport_;
  test_vendor_lib::TestChannelTransport remote_link_layer_transport_;

  test_vendor_lib::TestModel test_model_{
      [this](std::chrono::milliseconds delay, const test_vendor_lib::TaskCallback& task) {
        return async_manager_.ExecAsync(delay, task);
      },

      [this](std::chrono::milliseconds delay, std::chrono::milliseconds period,
             const test_vendor_lib::TaskCallback& task) {
        return async_manager_.ExecAsyncPeriodically(delay, period, task);
      },

      [this](test_vendor_lib::AsyncTaskId task) { async_manager_.CancelAsyncTask(task); },

      [this](const std::string& server, int port) { return ConnectToRemoteServer(server, port); }};

  test_vendor_lib::TestCommandHandler test_channel_{test_model_};
};

}  // namespace root_canal
}  // namespace bluetooth
}  // namespace android