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

Commit 2c6a7d03 authored by Dennis Cheng's avatar Dennis Cheng
Browse files

test_vendor_lib: Add working post delayed task

This upload fixes the post delayed task functionality so that it uses
a custom time stamped event object instead of posting callbacks to the
vendor manager's task runner (which does not support delayed tasks for
MessageLoopForIO).

Bug: 21586676
Change-Id: Ib5d9019d8c8dd5c9ad865df82eac16b0ecf50268
parent 0869c699
Loading
Loading
Loading
Loading
+5 −8
Original line number Diff line number Diff line
@@ -396,20 +396,17 @@ class DualModeController {
  // Clears all test channel modifications.
  void TestChannelClear(const std::vector<std::string>& args);

  // Sets the response delay for events to 0.
  void TestChannelClearEventDelay(const std::vector<std::string>& args);

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

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

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

  // Causes events to be sent after a delay.
  void TestChannelSetEventDelay(const std::vector<std::string>& args);

  // Sets the response delay for events to 0.
  void TestChannelClearEventDelay(const std::vector<std::string>& args);
  // Causes all future HCI commands to timeout.
  void TestChannelTimeoutAll(const std::vector<std::string>& args);

 private:
  // Current link layer state of the controller.
+30 −11
Original line number Diff line number Diff line
@@ -18,7 +18,7 @@

#include <functional>
#include <memory>
#include <queue>
#include <list>

extern "C" {
#include <sys/epoll.h>
@@ -59,10 +59,8 @@ class HciTransport : public base::MessageLoopForIO::Watcher {
      std::function<void(std::unique_ptr<CommandPacket>)> callback);

  // Posts the event onto |outbound_events_| to be written sometime in the
  // future when the vendor file descriptor is ready for writing. See
  // |outbound_events_| for why |event| is a shared pointer (as opposed to a
  // unique pointer).
  void PostEventResponse(std::shared_ptr<EventPacket> event);
  // future when the vendor file descriptor is ready for writing.
  void PostEventResponse(std::unique_ptr<EventPacket> event);

  // Posts the event onto |outbound_events_| after |delay| ms. A call to
  // |PostEventResponse| with |delay| 0 is equivalent to a call to |PostEvent|.
@@ -70,6 +68,29 @@ class HciTransport : public base::MessageLoopForIO::Watcher {
                                base::TimeDelta delay);

 private:
  // Wrapper class for sending events on a delay. The TimeStampedEvent object
  // takes ownership of a given event packet.
  class TimeStampedEvent {
   public:
    TimeStampedEvent(std::unique_ptr<EventPacket> event, base::TimeDelta delay);

    // Using this constructor is equivalent to calling the 2-argument
    // constructor with a |delay| of 0. It is used to generate event responses
    // with no delay.
    TimeStampedEvent(std::unique_ptr<EventPacket> event);

    const base::TimeTicks& GetTimeStamp() const;

    const EventPacket& GetEvent();

   private:
    std::shared_ptr<EventPacket> event_;

    // The time associated with the event, indicating the earliest time at which
    // |event_| will be sent.
    base::TimeTicks time_stamp_;
  };

  // base::MessageLoopForIO::Watcher overrides:
  void OnFileCanReadWithoutBlocking(int fd) override;

@@ -79,14 +100,12 @@ class HciTransport : public base::MessageLoopForIO::Watcher {
  // |command_handler_|, passing ownership of the command packet to the handler.
  void ReceiveReadyCommand() const;

  void AddEventToOutboundEvents(std::unique_ptr<TimeStampedEvent> event);

  // Write queue for sending events to the HCI. Event packets are removed from
  // the queue and written when write-readiness is signalled by the message
  // loop. Packets are shared pointers because the bind logic in
  // PostEventResponse forces copy semantics to be used on the bound arguments.
  // There will, however, be only a single instance of each EventPacket owned by
  // the HciTransport (i.e. the shared pointer objects are unique) and this will
  // be reinforced with CHECK's.
  std::queue<std::shared_ptr<EventPacket>> outbound_events_;
  // loop. After being written, the event packets are destructed.
  std::list<std::unique_ptr<TimeStampedEvent>> outbound_events_;

  // Callback executed in ReceiveReadyCommand() to pass the incoming command
  // over to the handler for further processing.
+13 −28
Original line number Diff line number Diff line
@@ -168,22 +168,24 @@ class TestChannelShell(cmd.Cmd):
    cmd.Cmd.__init__(self)
    self._test_channel = test_channel

  def do_quit(self, arg):
    """Exits the test channel."""
    self._connection.close()
    self._test_channel = test_channel
  def do_clear(self, args):
    """
    Arguments: None.
    Resets the controller to its original, unmodified state.
    """
    self._test_channel.send_command('CLEAR', [])

  def do_timeout_all(self, args):
  def do_clear_event_delay(self, args):
    """
    Arguments: None.
    Causes all HCI commands to timeout.
    Clears the response delay set by set_event_delay.
    """
    self._test_channel.send_command('TIMEOUT_ALL', [])
    self._test_channel.send_command('CLEAR_EVENT_DELAY', args.split())

  def do_discover(self, args):
    """
    Arguments: name_1 name_2 ...
    Sends an inquiry result for device(s) |name|. If no names are provided, a
    Sends an inquiry result for named device(s). If no names are provided, a
    random name is used instead.
    """
    if len(args) == 0:
@@ -196,16 +198,6 @@ class TestChannelShell(cmd.Cmd):
      device_names_and_addresses.append(device.get_address())
    self._test_channel.send_command('DISCOVER', device_names_and_addresses)

  def do_discover_interval(self, args):
    """
    Arguments: interval_in_ms
    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_set_event_delay(self, args):
    """
    Arguments: interval_in_ms
@@ -214,19 +206,12 @@ class TestChannelShell(cmd.Cmd):
    """
    self._test_channel.send_command('SET_EVENT_DELAY', args.split())

  def do_clear_event_delay(self, args):
    """
    Arguments: None.
    Clears the response delay set by |do_set_event_delay|.
    """
    self._test_channel.send_command('CLEAR_EVENT_DELAY', args.split())

  def do_clear(self, args):
  def do_timeout_all(self, args):
    """
    Arguments: None.
    Resets the controller to its original, unmodified state.
    Causes all HCI commands to timeout.
    """
    self._test_channel.send_command('CLEAR', [])
    self._test_channel.send_command('TIMEOUT_ALL', [])

  def do_quit(self, args):
    """
+2 −14
Original line number Diff line number Diff line
@@ -177,11 +177,10 @@ DualModeController::DualModeController()
  active_test_channel_commands_[command_name] = \
      std::bind(&DualModeController::method, this, std::placeholders::_1);
  SET_TEST_HANDLER("CLEAR", TestChannelClear);
  SET_TEST_HANDLER("CLEAR_EVENT_DELAY", TestChannelClearEventDelay);
  SET_TEST_HANDLER("DISCOVER", TestChannelDiscover);
  SET_TEST_HANDLER("DISCOVER_INTERVAL", TestChannelDiscoverInterval);
  SET_TEST_HANDLER("TIMEOUT_ALL", TestChannelTimeoutAll);
  SET_TEST_HANDLER("SET_EVENT_DELAY", TestChannelSetEventDelay);
  SET_TEST_HANDLER("CLEAR_EVENT_DELAY", TestChannelClearEventDelay);
  SET_TEST_HANDLER("TIMEOUT_ALL", TestChannelTimeoutAll);
#undef SET_TEST_HANDLER
}

@@ -258,17 +257,6 @@ void DualModeController::TestChannelDiscover(
  }
}

// TODO(dennischeng): This should discover devices continuously at the specified
// interval until the user sends an explicit 'stop' command. Each device should
// have a unique bd address and name?
void DualModeController::TestChannelDiscoverInterval(
    const std::vector<std::string>& args) {
  LogCommand("TestChannel Discover Interval");
  test_channel_state_ = kDelayedResponse;
  SetEventDelay(std::stoi(args[2]));
  SendExtendedInquiryResult(args[0], args[1]);
}

void DualModeController::TestChannelTimeoutAll(
    const std::vector<std::string>& args) {
  LogCommand("TestChannel Timeout All");
+53 −15
Original line number Diff line number Diff line
@@ -101,32 +101,70 @@ void HciTransport::RegisterCommandHandler(
void HciTransport::OnFileCanWriteWithoutBlocking(int fd) {
  CHECK(fd == GetVendorFd());
  if (!outbound_events_.empty()) {
    CHECK(outbound_events_.front().unique());
    packet_stream_.SendEvent(*(outbound_events_.front()), fd);
    outbound_events_.pop();
    base::TimeTicks current_time = base::TimeTicks::Now();
    auto it = outbound_events_.begin();
    // Check outbound events for events that can be sent, i.e. events with a
    // timestamp before the current time. Stop sending events when
    // |packet_stream_| fails writing.
    for (auto it = outbound_events_.begin(); it != outbound_events_.end();) {
      if ((*it)->GetTimeStamp() > current_time) {
        ++it;
        continue;
      }
      if (!packet_stream_.SendEvent((*it)->GetEvent(), fd))
        return;
      it = outbound_events_.erase(it);
    }
  }
}

void HciTransport::AddEventToOutboundEvents(
    std::unique_ptr<TimeStampedEvent> event) {
  outbound_events_.push_back(std::move(event));
}

void HciTransport::PostEventResponse(std::shared_ptr<EventPacket> event) {
  outbound_events_.push(event);
void HciTransport::PostEventResponse(std::unique_ptr<EventPacket> event) {
  AddEventToOutboundEvents(std::make_unique<TimeStampedEvent>(std::move(event)));
}

void HciTransport::PostDelayedEventResponse(std::unique_ptr<EventPacket> event,
                                            base::TimeDelta delay) {
  CHECK(base::MessageLoop::current());
  CHECK(base::ThreadTaskRunnerHandle::Get()->BelongsToCurrentThread());
  CHECK(base::ThreadTaskRunnerHandle::Get()->RunsTasksOnCurrentThread());
  // TODO(dennischeng): When it becomes available for MessageLoopForIO, use the
  // thread's task runner to post |PostEventResponse| as a delayed task, being
  // sure to CHECK the appropriate task runner attributes using
  // base::ThreadTaskRunnerHandle.

  // The system does not support high resolution timing and the clock could be
  // as coarse as ~15.6 ms so the event is sent without a delay to avoid
  // inconsistent event responses.
  if (!base::TimeTicks::IsHighResolution()) {
    LOG_INFO(LOG_TAG,
              "System does not support high resolution timing. Sending event "
              "without delay.");
    PostEventResponse(std::move(event));
  }

  LOG_INFO(LOG_TAG, "Posting event response with delay of %lld ms.",
           delay.InMilliseconds());

  if (!base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
          FROM_HERE, base::Bind(&HciTransport::PostEventResponse,
                                weak_ptr_factory_.GetWeakPtr(),
                                std::shared_ptr<EventPacket>(std::move(event))),
          delay)) {
    LOG_ERROR(LOG_TAG, "Couldn't post event response.");
  AddEventToOutboundEvents(
      std::make_unique<TimeStampedEvent>(std::move(event), delay));
}

HciTransport::TimeStampedEvent::TimeStampedEvent(
    std::unique_ptr<EventPacket> event, base::TimeDelta delay)
    : event_(std::move(event)), time_stamp_(base::TimeTicks::Now() + delay) {}

HciTransport::TimeStampedEvent::TimeStampedEvent(
    std::unique_ptr<EventPacket> event)
    : event_(std::move(event)), time_stamp_(base::TimeTicks::UnixEpoch()) {}

const base::TimeTicks& HciTransport::TimeStampedEvent::GetTimeStamp() const {
  return time_stamp_;
}

const EventPacket& HciTransport::TimeStampedEvent::GetEvent() {
  return *(event_.get());
}

}  // namespace test_vendor_lib
Loading