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

Commit bfd19f85 authored by Dennis Cheng's avatar Dennis Cheng
Browse files

test_vnd_lib: Discover devices

This upload allows the user to send fake inquiry responses to the
controller at run-time via the test channel.

Bug: 21586676
Change-Id: Ieae24d496115f19716fcaaffa627a9c22ffade21
parent 139a4d2e
Loading
Loading
Loading
Loading
+23 −3
Original line number Diff line number Diff line
@@ -324,7 +324,19 @@ class DualModeController {
  //           is halted.
  void HciInquiry(const std::vector<std::uint8_t>& args);

  void UciTimeoutAll(const std::vector<std::uint8_t>& args);
  // Test Channel commands:

  // Clears all test channel modifications.
  void UciClear(const std::vector<std::string>& args);

  // Discovers a fake device.
  void UciDiscover(const std::vector<std::string>& args);

  // Discovers a fake device on the specified interval (in ms).
  void UciDiscoverInterval(const std::vector<std::string>& args);

  // Causes all future HCI commands to timeout.
  void UciTimeoutAll(const std::vector<std::string>& args);

 private:
  enum State {
@@ -335,6 +347,11 @@ class DualModeController {
    kConnection,  // In a connection.
  };

  enum TestChannelState {
    kNone,  // The controller is running normally.
    kTimeoutAll,  // All commands should time out, i.e. send no response.
  };

  // Creates a command complete event and sends it back to the HCI.
  void SendCommandComplete(uint16_t command_opcode,
                           const std::vector<uint8_t>& return_parameters) const;
@@ -353,7 +370,8 @@ class DualModeController {
  void SendInquiryResult() const;

  // Sends an extended inquiry response for a fake device.
  void SendExtendedInquiryResult() const;
  void SendExtendedInquiryResult(const std::string& name,
                                 const std::string& address) const;

  // Callback provided to send events from the controller back to the HCI.
  std::function<void(std::unique_ptr<EventPacket>)> send_event_;
@@ -366,7 +384,7 @@ class DualModeController {
      active_commands_;

  std::unordered_map<std::string,
                     std::function<void(const std::vector<std::uint8_t>&)>>
                     std::function<void(const std::vector<std::string>&)>>
      test_channel_active_commands_;

  // Specifies the format of Inquiry Result events to be returned during the
@@ -380,6 +398,8 @@ class DualModeController {
  // Current link layer state of the controller.
  State state_;

  TestChannelState test_channel_state_;

  DISALLOW_COPY_AND_ASSIGN(DualModeController);
};

+3 −3
Original line number Diff line number Diff line
@@ -37,14 +37,14 @@ class TestChannelHandler {

  // Callback to be fired when a command is received from the test channel.
  void HandleTestCommand(std::string command_name,
                         std::vector<std::uint8_t> args);
                         std::vector<std::string> args);

  // Creates the mapping from the |command_name| to the method |callback|,
  // which is provided by the controller and will be fired when its command is
  // received from the test channel.
  void RegisterControllerCommand(
      std::string command_name,
      std::function<void(const std::vector<std::uint8_t> args)> callback);
      std::function<void(const std::vector<std::string> args)> callback);

  void RegisterHandlersWithTransport(TestChannelTransport& transport);

@@ -52,7 +52,7 @@ class TestChannelHandler {
  // Controller callbacks to be executed in handlers and registered in
  // RegisterControllerCommand().
  std::unordered_map<std::string,
                     std::function<void(const std::vector<std::uint8_t> args)> >
                     std::function<void(const std::vector<std::string> args)> >
      commands_;

  DISALLOW_COPY_AND_ASSIGN(TestChannelHandler);
+2 −2
Original line number Diff line number Diff line
@@ -52,7 +52,7 @@ class TestChannelTransport : public base::MessageLoopForIO::Watcher {
  // Sets the callback that fires when data is read in
  // |OnFileCanReadWithoutBlocking|.
  void RegisterCommandHandler(
      std::function<void(std::string, std::vector<std::uint8_t>)> callback);
      std::function<void(std::string, std::vector<std::string>)> callback);

 private:
  // base::MessageLoopForIO::Watcher overrides:
@@ -60,7 +60,7 @@ class TestChannelTransport : public base::MessageLoopForIO::Watcher {

  void OnFileCanWriteWithoutBlocking(int fd) override;

  std::function<void(std::string, std::vector<std::uint8_t>)>
  std::function<void(std::string, std::vector<std::string>)>
      command_handler_;

  // File descriptor to watch for test hook data.
+63 −5
Original line number Diff line number Diff line
@@ -44,6 +44,18 @@ import string
import struct
import sys

DEVICE_NAME_LENGTH = 6
DEVICE_ADDRESS_LENGTH = 6

# Used to generate fake device names and addresses during discovery.
def generate_random_name():
  return ''.join(random.SystemRandom().choice(string.ascii_uppercase + \
    string.digits) for _ in range(DEVICE_NAME_LENGTH))

def generate_random_address():
  return ''.join(random.SystemRandom().choice(string.digits) for _ in \
    range(DEVICE_ADDRESS_LENGTH))

class Connection(object):
  """Simple wrapper class for a socket object.

@@ -71,6 +83,12 @@ class TestChannel(object):

  def __init__(self, address, port):
    self._connection = Connection(address, port)
    self._discovered_devices = DeviceManager()

  def discover_new_device(self, name=None, address=None):
    device = Device(name, address)
    self._discovered_devices.add_device(device)
    return device

  def close(self):
    self._connection.close()
@@ -99,6 +117,40 @@ class TestChannel(object):
      if len(arg) > 255:
        raise ValueError  # Size must be encodable in one octet.

class DeviceManager(object):
  """Maintains the active fake devices that have been "discovered".

  Attributes:
    device_list: Maps device addresses (keys) to devices (values).
  """

  def __init__(self):
    self.device_list = {}

  def add_device(self, device):
    self.device_list[device.get_address()] = device

class Device(object):
  """A fake device to be returned in inquiry and scan results. Note that if an
  explicit name or address is not provided, a random string of characters
  is used.

  Attributes:
    name: The device name for use in extended results.
    address: The BD address of the device.
  """

  def __init__(self, name=None, address=None):
    # TODO(dennischeng): Generate device properties more robustly.
    self.name = generate_random_name() if name is None else name
    self.address = generate_random_address() if address is None else address

  def get_name(self):
    return self.name

  def get_address(self):
    return self.address

class TestChannelShell(cmd.Cmd):
  """Shell for sending test channel data to controller.

@@ -139,10 +191,14 @@ class TestChannelShell(cmd.Cmd):
    random name is used instead.
    """
    if len(args) == 0:
      # TODO(dennischeng): Generate device properties more robustly.
      args = ''.join(random.SystemRandom().choice(string.ascii_uppercase + \
      string.digits) for _ in range(6))
    self._test_channel.send_command('DISCOVER', args.split())
      args = generate_random_name()
    device_list = [self.test_channel.discover_new_device(arg) for arg in \
                   args.split()]
    device_names_and_addresses = []
    for device in device_list:
      device_names_and_addresses.append(device.get_name())
      device_names_and_addresses.append(device.get_address())
    self._test_channel.send_command('DISCOVER', device_names_and_addresses)

  def do_discover_interval(self, args):
    """
@@ -150,6 +206,8 @@ class TestChannelShell(cmd.Cmd):
    Sends an inquiry result for a device with a random name on the interval
    specified by |interval_in_ms|.
    """
    args.append(generate_random_name())
    args.append(generate_random_address())
    self._test_channel.send_command('DISCOVER_INTERVAL', args.split())

  def do_clear(self, args):
+34 −9
Original line number Diff line number Diff line
@@ -156,22 +156,26 @@ void DualModeController::SendInquiryResult() const {
  send_event_(std::move(inquiry_result));
}

void DualModeController::SendExtendedInquiryResult() const {
void DualModeController::SendExtendedInquiryResult(
    const std::string& name, const std::string& address) const {
  std::vector<uint8_t> rssi = {0};
  std::vector<uint8_t> extended_inquiry_data = {7, 0x09,
                                                'F', 'o', 'o', 'B', 'a', 'r'};
  std::vector<uint8_t> extended_inquiry_data = {name.length() + 1, 0x09};
  std::copy(name.begin(), name.end(),
            std::back_inserter(extended_inquiry_data));
  std::vector<uint8_t> bd_address(address.begin(), address.end());
  // TODO(dennischeng): Use constants for parameter sizes, here and elsewhere.
  while (extended_inquiry_data.size() < 240) {
    extended_inquiry_data.push_back(0);
  }
  std::unique_ptr<EventPacket> extended_inquiry_result =
      EventPacket::CreateExtendedInquiryResultEvent(
          kOtherDeviceBdAddress, kPageScanRepetitionMode, kPageScanPeriodMode,
          kClassOfDevice, kClockOffset, rssi, extended_inquiry_data);
          bd_address, kPageScanRepetitionMode, kPageScanPeriodMode, kClassOfDevice,
          kClockOffset, rssi, extended_inquiry_data);
  send_event_(std::move(extended_inquiry_result));
}

DualModeController::DualModeController() : state_(kStandby) {
DualModeController::DualModeController()
    : state_(kStandby), test_channel_state_(kNone) {
#define SET_HANDLER(opcode, method) \
  active_commands_[opcode] =        \
      std::bind(&DualModeController::method, this, std::placeholders::_1);
@@ -205,7 +209,10 @@ DualModeController::DualModeController() : state_(kStandby) {
#define SET_TEST_HANDLER(command_name, method)  \
  test_channel_active_commands_[command_name] = \
      std::bind(&DualModeController::method, this, std::placeholders::_1);
  SET_TEST_HANDLER("TimeoutAll", UciTimeoutAll);
  SET_TEST_HANDLER("CLEAR", UciTimeoutAll);
  SET_TEST_HANDLER("DISCOVER", UciDiscover);
  SET_TEST_HANDLER("DISCOVER_INTERVAL", UciTimeoutAll);
  SET_TEST_HANDLER("TIMEOUT_ALL", UciTimeoutAll);
#undef SET_TEST_HANDLER
}

@@ -228,8 +235,26 @@ void DualModeController::RegisterEventChannel(
  send_event_ = callback;
}

void DualModeController::UciTimeoutAll(const std::vector<std::uint8_t>& args) {
void DualModeController::UciClear(const std::vector<std::string>& args) {
  LogCommand("Uci Clear");
  test_channel_state_ = kNone;
}

void DualModeController::UciDiscover(const std::vector<std::string>& args) {
  LogCommand("Uci Discover");
  for (size_t i = 0; i < args.size()-1; i+=2) {
    SendExtendedInquiryResult(args[i], args[i+1]);
  }
}

void DualModeController::UciDiscoverInterval(
    const std::vector<std::string>& args) {
  LogCommand("Uci Timeout All");
}

void DualModeController::UciTimeoutAll(const std::vector<std::string>& args) {
  LogCommand("Uci Timeout All");
  test_channel_state_ = kTimeoutAll;
}

// TODO(dennischeng): Store relevant arguments from commands as attributes of
@@ -405,7 +430,7 @@ void DualModeController::HciInquiry(const std::vector<uint8_t>& /* args */) {
      break;

    case (kExtendedOrRssiInquiry):
      SendExtendedInquiryResult();
      SendExtendedInquiryResult("FooBar", "123456");
      break;
  }
}
Loading