// 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 {
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;
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_;
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
// Current link layer state of the controller.
State state_;
+ TestChannelState test_channel_state_;
+
DISALLOW_COPY_AND_ASSIGN(DualModeController);
};
// 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);
// 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);
// 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:
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.
the port, also from step 1, as arguments.
"""
-#!/ usr / bin / env python
+#!/usr/bin/env python
import cmd
import random
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.
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()
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.
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):
"""
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):
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);
#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
}
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
break;
case (kExtendedOrRssiInquiry):
- SendExtendedInquiryResult();
+ SendExtendedInquiryResult("FooBar", "123456");
break;
}
}
}
void TestChannelHandler::HandleTestCommand(std::string command_name,
- std::vector<uint8_t> args) {
+ std::vector<std::string> args) {
LOG_INFO(LOG_TAG, "Test Channel command: %s", command_name.c_str());
// The command hasn't been registered with the handler yet. There is nothing
if (commands_.count(command_name) == 0) {
return;
}
- std::function<void(const std::vector<uint8_t> args)> command =
+ std::function<void(const std::vector<std::string> args)> command =
commands_[command_name];
command(args);
}
void TestChannelHandler::RegisterControllerCommand(
std::string command_name,
- std::function<void(const std::vector<uint8_t> args)> callback) {
+ std::function<void(const std::vector<std::string> args)> callback) {
commands_[command_name] = callback;
}
LOG_INFO(LOG_TAG, "Event ready in TestChannelTransport on fd: %d", fd);
uint8_t command_name_size = 0;
read(fd, &command_name_size, 1);
- LOG_INFO(LOG_TAG, "command_name_size: %d", command_name_size);
- std::vector<uint8_t> command_name;
- command_name.resize(command_name_size);
- read(fd, &command_name[0], command_name_size);
- std::string command_name_str(command_name.begin(), command_name.end());
+ std::vector<uint8_t> command_name_raw;
+ command_name_raw.resize(command_name_size);
+ read(fd, &command_name_raw[0], command_name_size);
+ std::string command_name(command_name_raw.begin(), command_name_raw.end());
LOG_INFO(LOG_TAG, "Received command from test channel: %s",
- command_name_str.c_str());
+ command_name.c_str());
- if (command_name_str == "CLOSE_TEST_CHANNEL") {
+ if (command_name == "CLOSE_TEST_CHANNEL") {
fd_.reset(nullptr);
return;
}
LOG_INFO(LOG_TAG, "Command argument %d: %s", i, args[i].c_str());
}
+ command_handler_(command_name, args);
}
void TestChannelTransport::OnFileCanWriteWithoutBlocking(int fd) {}
void TestChannelTransport::RegisterCommandHandler(
- std::function<void(std::string, std::vector<uint8_t>)> callback) {
+ std::function<void(std::string, std::vector<std::string>)> callback) {
command_handler_ = callback;
}