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

Commit e80bc288 authored by Erwin Jansen's avatar Erwin Jansen Committed by Gerrit Code Review
Browse files

Merge changes Ia8bacbbd,Ie1c2ac05,I3a8f2419

* changes:
  Add a h4 packetizer for data channels.
  Introduce abstract datachannels in Root Canal.
  Separate H4 packet parsing from data retrieval.
parents 25395d74 3c23fcb2
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -31,7 +31,9 @@ cc_library_static {
        "model/devices/classic.cc",
        "model/devices/device.cc",
        "model/devices/device_properties.cc",
        "model/devices/h4_data_channel_packetizer.cc",
        "model/devices/h4_packetizer.cc",
        "model/devices/h4_parser.cc",
        "model/devices/hci_protocol.cc",
        "model/devices/hci_socket_device.cc",
        "model/devices/keyboard.cc",
@@ -47,6 +49,9 @@ cc_library_static {
        "model/setup/test_channel_transport.cc",
        "model/setup/test_command_handler.cc",
        "model/setup/test_model.cc",
        "net/posix/posix_async_socket.cc",
        "net/posix/posix_async_socket_connector.cc",
        "net/posix/posix_async_socket_server.cc",
        ":BluetoothPacketSources",
        ":BluetoothHciClassSources",
    ],
@@ -100,6 +105,8 @@ cc_test_host {
    ],
    srcs: [
        "test/async_manager_unittest.cc",
        "test/h4_parser_unittest.cc",
        "test/posix_socket_unittest.cc",
        "test/security_manager_unittest.cc",
    ],
    header_libs: [
+95 −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.
//

#include "h4_data_channel_packetizer.h"

#include <string.h>     // for strerror, size_t
#include <unistd.h>     // for ssize_t
#include <cerrno>       // for errno, EAGAIN, ECONNRESET
#include <cstdint>      // for uint8_t
#include <functional>   // for function
#include <type_traits>  // for remove_extent_t
#include <utility>      // for move
#include <vector>       // for vector

#include "model/devices/h4_parser.h"  // for H4Parser, ClientDisconnectCa...
#include "model/devices/hci_protocol.h"  // for PacketReadCallback, AsyncDataChannel
#include "net/async_data_channel.h"      // for AsyncDataChannel
#include "os/log.h"                      // for LOG_ERROR, LOG_ALWAYS_FATAL

namespace test_vendor_lib {

H4DataChannelPacketizer::H4DataChannelPacketizer(
    std::shared_ptr<AsyncDataChannel> socket, PacketReadCallback command_cb,
    PacketReadCallback event_cb, PacketReadCallback acl_cb,
    PacketReadCallback sco_cb, PacketReadCallback iso_cb,
    ClientDisconnectCallback disconnect_cb)
    : uart_socket_(socket),
      h4_parser_(command_cb, event_cb, acl_cb, sco_cb, iso_cb),
      disconnect_cb_(std::move(disconnect_cb)) {}

size_t H4DataChannelPacketizer::Send(uint8_t type, const uint8_t* data,
                                     size_t length) {
  ssize_t ret = uart_socket_->Send(&type, sizeof(type));
  if (ret == -1) {
    LOG_ERROR("Error writing to UART (%s)", strerror(errno));
  }
  size_t to_be_written = ret;

  ret = uart_socket_->Send(data, length);
  if (ret == -1) {
    LOG_ERROR("Error writing to UART (%s)", strerror(errno));
  }
  to_be_written += ret;

  if (to_be_written != length + sizeof(type)) {
    LOG_ERROR("%d / %d bytes written - something went wrong...",
              static_cast<int>(to_be_written),
              static_cast<int>(length + sizeof(type)));
  }
  return to_be_written;
}

void H4DataChannelPacketizer::OnDataReady(
    std::shared_ptr<AsyncDataChannel> socket) {
  if (!socket->Connected()) return;
  ssize_t bytes_to_read = h4_parser_.BytesRequested();
  std::vector<uint8_t> buffer(bytes_to_read);

  ssize_t bytes_read = socket->Recv(buffer.data(), bytes_to_read);
  if (bytes_read == 0) {
    LOG_INFO("remote disconnected!");
    disconnected_ = true;
    disconnect_cb_();
    return;
  } else if (bytes_read < 0) {
    if (errno == EAGAIN) {
      // No data, try again later.
      return;
    } else if (errno == ECONNRESET) {
      // They probably rejected our packet
      disconnected_ = true;
      disconnect_cb_();
      return;
    } else {
      LOG_ALWAYS_FATAL("Read error in %u: %s", h4_parser_.CurrentState(),
                       strerror(errno));
    }
  }
  h4_parser_.Consume(buffer.data(), bytes_read);
}

}  // namespace test_vendor_lib
+55 −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 <stddef.h>  // for size_t
#include <stdint.h>  // for uint8_t

#include <memory>  // for shared_ptr

#include "h4_parser.h"     // for ClientDisconnectCallback, H4Parser
#include "hci_protocol.h"  // for PacketReadCallback, AsyncDataChannel, HciProtocol
#include "net/async_data_channel.h"  // for AsyncDataChannel

namespace test_vendor_lib {

using android::net::AsyncDataChannel;

// A socket based H4DataChannelPacketizer. Call OnDataReady whenever
// data can be read from the socket.
class H4DataChannelPacketizer : public HciProtocol {
 public:
  H4DataChannelPacketizer(std::shared_ptr<AsyncDataChannel> socket,
                          PacketReadCallback command_cb,
                          PacketReadCallback event_cb,
                          PacketReadCallback acl_cb, PacketReadCallback sco_cb,
                          PacketReadCallback iso_cb,
                          ClientDisconnectCallback disconnect_cb);

  size_t Send(uint8_t type, const uint8_t* data, size_t length) override;

  void OnDataReady(std::shared_ptr<AsyncDataChannel> socket);

 private:
  std::shared_ptr<AsyncDataChannel> uart_socket_;
  H4Parser h4_parser_;

  ClientDisconnectCallback disconnect_cb_;
  bool disconnected_{false};
};

}  // namespace test_vendor_lib
+10 −125
Original line number Diff line number Diff line
@@ -17,37 +17,15 @@
#include "h4_packetizer.h"

#include <cerrno>

#include <dlfcn.h>
#include <fcntl.h>
#include <sys/uio.h>
#include <unistd.h>

#include "os/log.h"
#include "osi/include/osi.h"

namespace test_vendor_lib {
size_t H4Packetizer::HciGetPacketLengthForType(PacketType type,
                                               const uint8_t* preamble) const {
  static const size_t
      packet_length_offset[static_cast<size_t>(PacketType::ISO) + 1] = {
          0,
          H4Packetizer::COMMAND_LENGTH_OFFSET,
          H4Packetizer::ACL_LENGTH_OFFSET,
          H4Packetizer::SCO_LENGTH_OFFSET,
          H4Packetizer::EVENT_LENGTH_OFFSET,
          H4Packetizer::ISO_LENGTH_OFFSET,
      };

  size_t offset = packet_length_offset[static_cast<size_t>(type)];
  size_t size = preamble[offset];
  if (type == PacketType::ACL) {
    size |= ((size_t)preamble[offset + 1]) << 8u;
  }
  if (type == PacketType::ISO) {
    size |= ((size_t)preamble[offset + 1] & 0x0fu) << 8u;
  }
  return size;
}

H4Packetizer::H4Packetizer(int fd, PacketReadCallback command_cb,
                           PacketReadCallback event_cb,
@@ -55,18 +33,14 @@ H4Packetizer::H4Packetizer(int fd, PacketReadCallback command_cb,
                           PacketReadCallback iso_cb,
                           ClientDisconnectCallback disconnect_cb)
    : uart_fd_(fd),
      command_cb_(std::move(command_cb)),
      event_cb_(std::move(event_cb)),
      acl_cb_(std::move(acl_cb)),
      sco_cb_(std::move(sco_cb)),
      iso_cb_(std::move(iso_cb)),
      h4_parser_(command_cb, event_cb, acl_cb, sco_cb, iso_cb),
      disconnect_cb_(std::move(disconnect_cb)) {}

size_t H4Packetizer::Send(uint8_t type, const uint8_t* data, size_t length) {
  struct iovec iov[] = {{&type, sizeof(type)}, {const_cast<uint8_t*>(data), length}};
  ssize_t ret = 0;
  do {
    ret = TEMP_FAILURE_RETRY(writev(uart_fd_, iov, sizeof(iov) / sizeof(iov[0])));
    OSI_NO_INTR(ret = writev(uart_fd_, iov, sizeof(iov) / sizeof(iov[0])));
  } while (-1 == ret && EAGAIN == errno);

  if (ret == -1) {
@@ -78,59 +52,14 @@ size_t H4Packetizer::Send(uint8_t type, const uint8_t* data, size_t length) {
  return ret;
}

void H4Packetizer::OnPacketReady() {
  switch (hci_packet_type_) {
    case PacketType::COMMAND:
      command_cb_(packet_);
      break;
    case PacketType::ACL:
      acl_cb_(packet_);
      break;
    case PacketType::SCO:
      sco_cb_(packet_);
      break;
    case PacketType::EVENT:
      event_cb_(packet_);
      break;
    case PacketType::ISO:
      iso_cb_(packet_);
      break;
    default:
      LOG_ALWAYS_FATAL("Unimplemented packet type %d",
                       static_cast<int>(hci_packet_type_));
  }
  // Get ready for the next type byte.
  hci_packet_type_ = PacketType::UNKNOWN;
}

void H4Packetizer::OnDataReady(int fd) {
  if (disconnected_) return;
  ssize_t bytes_to_read = 0;
  uint8_t* buffer_pointer = nullptr;

  static const size_t preamble_size[static_cast<size_t>(PacketType::ISO) + 1] =
      {
          0,
          H4Packetizer::COMMAND_PREAMBLE_SIZE,
          H4Packetizer::ACL_PREAMBLE_SIZE,
          H4Packetizer::SCO_PREAMBLE_SIZE,
          H4Packetizer::EVENT_PREAMBLE_SIZE,
          H4Packetizer::ISO_PREAMBLE_SIZE,
      };
  switch (state_) {
    case HCI_TYPE:
      bytes_to_read = 1;
      buffer_pointer = &packet_type_;
      break;
    case HCI_PREAMBLE:
    case HCI_PAYLOAD:
      bytes_to_read = packet_.size() - bytes_read_;
      buffer_pointer = packet_.data() + bytes_read_;
      break;
  }
  ssize_t bytes_to_read = h4_parser_.BytesRequested();
  std::vector<uint8_t> buffer(bytes_to_read);

  ssize_t bytes_read =
      TEMP_FAILURE_RETRY(read(fd, buffer_pointer, bytes_to_read));
  ssize_t bytes_read;
  OSI_NO_INTR(bytes_read = read(fd, buffer.data(), bytes_to_read));
  if (bytes_read == 0) {
    LOG_INFO("remote disconnected!");
    disconnected_ = true;
@@ -146,55 +75,11 @@ void H4Packetizer::OnDataReady(int fd) {
      disconnect_cb_();
      return;
    } else {
      LOG_ALWAYS_FATAL(
          "Read error in %s: %s",
          state_ == HCI_TYPE
              ? "HCI_TYPE"
              : state_ == HCI_PREAMBLE ? "HCI_PREAMBLE" : "HCI_PAYLOAD",
      LOG_ALWAYS_FATAL("Read error in %d: %s", h4_parser_.CurrentState(),
                       strerror(errno));
    }
  } else if (bytes_read > bytes_to_read) {
    LOG_ALWAYS_FATAL("More bytes read (%u) than expected (%u)!",
                     static_cast<int>(bytes_read),
                     static_cast<int>(bytes_to_read));
  }

  switch (state_) {
    case HCI_TYPE:
      hci_packet_type_ = static_cast<PacketType>(packet_type_);
      if (hci_packet_type_ != PacketType::ACL &&
          hci_packet_type_ != PacketType::SCO &&
          hci_packet_type_ != PacketType::COMMAND &&
          hci_packet_type_ != PacketType::EVENT &&
          hci_packet_type_ != PacketType::ISO) {
        LOG_ALWAYS_FATAL("Unimplemented packet type %hhd", packet_type_);
      }
      state_ = HCI_PREAMBLE;
      bytes_read_ = 0;
      packet_.resize(preamble_size[static_cast<size_t>(hci_packet_type_)]);
      break;
    case HCI_PREAMBLE:
      bytes_read_ += bytes_read;
      if (bytes_read_ == packet_.size()) {
        size_t payload_size =
            HciGetPacketLengthForType(hci_packet_type_, packet_.data());
        if (payload_size == 0) {
          OnPacketReady();
          state_ = HCI_TYPE;
        } else {
          packet_.resize(packet_.size() + payload_size);
          state_ = HCI_PAYLOAD;
        }
      }
      break;
    case HCI_PAYLOAD:
      bytes_read_ += bytes_read;
      if (bytes_read_ == packet_.size()) {
        OnPacketReady();
        state_ = HCI_TYPE;
      }
      break;
  }
  h4_parser_.Consume(buffer.data(), bytes_read);
}

}  // namespace test_vendor_lib
+6 −52
Original line number Diff line number Diff line
@@ -19,22 +19,15 @@
#include <functional>
#include <vector>

#include "h4_parser.h"
#include "hci_protocol.h"

namespace test_vendor_lib {

using HciPacketReadyCallback = std::function<void(void)>;
using ClientDisconnectCallback = std::function<void()>;

enum class PacketType : uint8_t {
  UNKNOWN = 0,
  COMMAND = 1,
  ACL = 2,
  SCO = 3,
  EVENT = 4,
  ISO = 5,
};

// A socket based H4Packetizer. Call OnDataReady whenever
// data can be read from file descriptor fd.
//
// This is only supported on unix.
class H4Packetizer : public HciProtocol {
 public:
  H4Packetizer(int fd, PacketReadCallback command_cb,
@@ -44,53 +37,14 @@ class H4Packetizer : public HciProtocol {

  size_t Send(uint8_t type, const uint8_t* data, size_t length) override;

  void OnPacketReady();

  void OnDataReady(int fd);

  // 2 bytes for opcode, 1 byte for parameter length (Volume 2, Part E, 5.4.1)
  static constexpr size_t COMMAND_PREAMBLE_SIZE = 3;
  static constexpr size_t COMMAND_LENGTH_OFFSET = 2;

  // 2 bytes for handle, 2 bytes for data length (Volume 2, Part E, 5.4.2)
  static constexpr size_t ACL_PREAMBLE_SIZE = 4;
  static constexpr size_t ACL_LENGTH_OFFSET = 2;

  // 2 bytes for handle, 1 byte for data length (Volume 2, Part E, 5.4.3)
  static constexpr size_t SCO_PREAMBLE_SIZE = 3;
  static constexpr size_t SCO_LENGTH_OFFSET = 2;

  // 1 byte for event code, 1 byte for parameter length (Volume 2, Part
  // E, 5.4.4)
  static constexpr size_t EVENT_PREAMBLE_SIZE = 2;
  static constexpr size_t EVENT_LENGTH_OFFSET = 1;

  // 2 bytes for handle and flags, 12 bits for length (Volume 2, Part E, 5.4.5)
  static constexpr size_t ISO_PREAMBLE_SIZE = 4;
  static constexpr size_t ISO_LENGTH_OFFSET = 2;

 private:
  int uart_fd_;

  PacketReadCallback command_cb_;
  PacketReadCallback event_cb_;
  PacketReadCallback acl_cb_;
  PacketReadCallback sco_cb_;
  PacketReadCallback iso_cb_;
  H4Parser h4_parser_;

  ClientDisconnectCallback disconnect_cb_;
  bool disconnected_{false};

  size_t HciGetPacketLengthForType(PacketType type,
                                   const uint8_t* preamble) const;

  PacketType hci_packet_type_{PacketType::UNKNOWN};

  enum State { HCI_TYPE, HCI_PREAMBLE, HCI_PAYLOAD };
  State state_{HCI_TYPE};
  uint8_t packet_type_{};
  std::vector<uint8_t> packet_;
  size_t bytes_read_{0};
};

}  // namespace test_vendor_lib
Loading