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

Commit 2fd97704 authored by Ajay Panicker's avatar Ajay Panicker
Browse files

Add Packet class as alternative to BT_HDR

Adds Packets, Iterators, and Packet Builders to Bluetooth to be used as
an alternative to BT_HDR.

 - Packet is a base class used to pass around data. It is intended to be
   immutable after creation and subclasses will implement all required
   functions and their own accessor methods to retrieve data.

 - Iterator provides a convienent way to traverse packet data.

 - PacketBuilder is used to construct Packets. This class is should be
   the only way to mutate packet objects.

Also add class representations for the following packet types:
  AVRCP (Base Packet)
    AVRCP Vendor Packet
      AVRCP Get Capabilities Packet
      AVRCP Get Element Attributes Packet
      AVRCP Register Notification Packet
      AVRCP Get Play Status Packet
      AVRCP Reject Packet
    AVRCP Pass Through Packet

Adds net_test_btpackets to run_host_unit_tests.py for pre-submit

Bug: 68854188
Test: run host native test net_test_packets
Change-Id: I0d385710178e5feb2d5089847580754ad6308709
parent 6d5f4b99
Loading
Loading
Loading
Loading
+37 −0
Original line number Diff line number Diff line
cc_library_static {
    name: "lib-bt-packets",
    host_supported: true,
    export_include_dirs: [
        "./include",
        "./",
    ],
    whole_static_libs: [
        "lib-bt-packets-base",
        "lib-bt-packets-avrcp",
    ],
}

cc_test {
    name: "net_test_btpackets",
    defaults: ["fluoride_defaults"],
    host_supported: true,
    local_include_dirs: ["tests"],
    srcs: [
        "tests/base/packet_test.cc",
        "tests/base/iterator_test.cc",
        "tests/base/packet_builder_test.cc",
        "tests/avrcp/avrcp_packet_test.cc",
        "tests/avrcp/vendor_packet_test.cc",
        "tests/avrcp/get_capabilities_packet_test.cc",
        "tests/avrcp/get_element_attributes_packet_test.cc",
        "tests/avrcp/get_play_status_packet_test.cc",
        "tests/avrcp/pass_through_packet_test.cc",
        "tests/avrcp/register_notification_packet_test.cc",
        "tests/avrcp/avrcp_reject_packet_test.cc",
    ],
    static_libs: [
        "libgmock",
        "lib-bt-packets",
    ],
    cflags: ["-DBUILDCFG","-g"],
}
 No newline at end of file
+26 −0
Original line number Diff line number Diff line
cc_library_static {
    name: "lib-bt-packets-avrcp",
    export_include_dirs: ["."],
    host_supported: true,
    srcs: [
        "avrcp_packet.cc",
        "vendor_packet.cc",
        "capabilities_packet.cc",
        "get_element_attributes_packet.cc",
        "get_play_status_packet.cc",
        "pass_through_packet.cc",
        "register_notification_packet.cc",
        "avrcp_reject_packet.cc",
    ],
    shared_libs: [
        "libchrome",
    ],
    static_libs: [
        "lib-bt-packets-base",
    ],
    cflags: [
        "-Wall",
        "-Wextra",
        "-Werror",
    ],
}
+117 −0
Original line number Diff line number Diff line
/*
 * Copyright 2018 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 <base/sys_byteorder.h>

// This file contains the different AVRCP Constants
namespace bluetooth {
namespace avrcp {

constexpr uint32_t BLUETOOTH_COMPANY_ID = 0x001958;

enum class CType : uint8_t {
  CONTROL = 0x0,
  STATUS = 0x1,
  NOTIFY = 0x3,
  ACCEPTED = 0x9,
  REJECTED = 0xa,
  STABLE = 0xc,
  CHANGED = 0xd,
  INTERIM = 0xf,
};

enum class Opcode : uint8_t {
  VENDOR = 0x00,
  UNIT_INFO = 0x30,
  SUBUNIT_INFO = 0x31,
  PASS_THROUGH = 0x7c,
};

// Found in AVRCP_v1.6.1 Section 4.5 Table 4.5
// Searching can be done in the spec by Camel Casing the constant name
enum class CommandPdu : uint8_t {
  GET_CAPABILITIES = 0x10,
  LIST_APPLICATION_SETTING_ATTRIBUTES = 0x11,
  GET_ELEMENT_ATTRIBUTES = 0x20,
  GET_PLAY_STATUS = 0x30,
  REGISTER_NOTIFICATION = 0x31,
};

enum class PacketType : uint8_t {
  SINGLE = 0x00,
};

enum class Capability : uint8_t {
  COMPANY_ID = 0x02,
  EVENTS_SUPPORTED = 0x03,
};

// Found in AVRCP_v1.6.1 Section 28 Appendix H
enum class Event : uint8_t {
  PLAYBACK_STATUS_CHANGED = 0x01,
  TRACK_CHANGED = 0x02,
  PLAYBACK_POS_CHANGED = 0x05,
  PLAYER_APPLICATION_SETTING_CHANGED = 0x08,
  NOW_PLAYING_CONTENT_CHANGED = 0x09,
  AVAILABLE_PLAYERS_CHANGED = 0x0a,
  ADDRESSED_PLAYER_CHANGED = 0x0b,
  UIDS_CHANGED = 0x0c,
  VOLUME_CHANGED = 0x0d,
};

enum class Attribute : uint32_t {
  TITLE = 0x01,
  ARTIST_NAME,
  ALBUM_NAME,
  TRACK_NUMBER,
  TOTAL_NUMBER_OF_TRACKS,
  GENRE,
  PLAYING_TIME,
  DEFAULT_COVER_ART,
};

enum class Status : uint8_t {
  INVALID_COMMAND = 0x00,
  INVALID_PARAMETER,
  PARAMETER_CONTENT_ERROR,
  INTERNAL_ERROR,
  NO_ERROR,
  UIDS_CHANGED,
  RESERVED,
  INVALID_DIRECTION,
  NOT_A_DIRECTORY,
  DOES_NOT_EXIST,
  INVALID_SCOPE,
  RANGE_OUT_OF_BOUNDS,
  FOLDER_ITEM_NOT_PLAYABLE,
  MEDIA_IN_USE,
  NOW_PLAYING_LIST_FULL,
  SEARCH_NOT_SUPPORTED,
  SEARCH_IN_PROGRESS,
  INVALID_PLAYER_ID,
  PLAYER_NOT_BROWSABLE,
  PLAYER_NOT_ADDRESSED,
  NO_VALID_SEARCH_RESULTS,
  NO_AVAILABLE_PLAYERS,
  ADDRESSED_PLAYER_CHANGED,
};

using AttributeEntry = std::pair<Attribute, std::string>;

}  // namespace avrcp
}  // namespace bluetooth
 No newline at end of file
+194 −0
Original line number Diff line number Diff line
/*
 * Copyright 2018 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 <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <type_traits>

#include "avrcp_common.h"

// We have our own definition of loghex to avoid dependencies
namespace {
template <typename T>
std::string loghex(T x) {
  std::stringstream tmp;
  tmp << "0x" << std::internal << std::hex << std::setfill('0')
      << std::setw(sizeof(T) * 2) << (unsigned int)x;
  return tmp.str();
}
}  // namespace

namespace bluetooth {
namespace avrcp {

#define CASE_RETURN_TEXT(code) \
  case code:                   \
    return #code

inline std::string CTypeText(const CType& type) {
  switch (type) {
    CASE_RETURN_TEXT(CType::CONTROL);
    CASE_RETURN_TEXT(CType::STATUS);
    CASE_RETURN_TEXT(CType::NOTIFY);
    CASE_RETURN_TEXT(CType::ACCEPTED);
    CASE_RETURN_TEXT(CType::REJECTED);
    CASE_RETURN_TEXT(CType::STABLE);
    CASE_RETURN_TEXT(CType::CHANGED);
    CASE_RETURN_TEXT(CType::INTERIM);
    default:
      return "Unknown CType: " + loghex(type);
  }
}

inline std::ostream& operator<<(std::ostream& os, const CType& type) {
  return os << CTypeText(type);
}

inline std::string OpcodeText(const Opcode& opcode) {
  switch (opcode) {
    CASE_RETURN_TEXT(Opcode::VENDOR);
    CASE_RETURN_TEXT(Opcode::UNIT_INFO);
    CASE_RETURN_TEXT(Opcode::SUBUNIT_INFO);
    CASE_RETURN_TEXT(Opcode::PASS_THROUGH);
    default:
      return "Unknown Opcode: " + loghex(opcode);
  }
}

inline std::ostream& operator<<(std::ostream& os, const Opcode& opcode) {
  return os << OpcodeText(opcode);
}

inline std::string CommandPduText(const CommandPdu& pdu) {
  switch (pdu) {
    CASE_RETURN_TEXT(CommandPdu::GET_CAPABILITIES);
    CASE_RETURN_TEXT(CommandPdu::LIST_APPLICATION_SETTING_ATTRIBUTES);
    CASE_RETURN_TEXT(CommandPdu::GET_ELEMENT_ATTRIBUTES);
    CASE_RETURN_TEXT(CommandPdu::GET_PLAY_STATUS);
    CASE_RETURN_TEXT(CommandPdu::REGISTER_NOTIFICATION);
    default:
      return "Unknown Command PDU: " + loghex(pdu);
  }
}

inline std::ostream& operator<<(std::ostream& os, const CommandPdu& pdu) {
  return os << CommandPduText(pdu);
}

inline std::string PacketTypeText(const PacketType& type) {
  switch (type) {
    CASE_RETURN_TEXT(PacketType::SINGLE);
    default:
      return "Unknown Packet Type: " + loghex(type);
  }
}

inline std::ostream& operator<<(std::ostream& os, const PacketType& type) {
  return os << PacketTypeText(type);
}

inline std::string CapabilityText(const Capability& cap) {
  switch (cap) {
    CASE_RETURN_TEXT(Capability::COMPANY_ID);
    CASE_RETURN_TEXT(Capability::EVENTS_SUPPORTED);
    default:
      return "Unknown Capability: " + loghex(cap);
  }
}

inline std::ostream& operator<<(std::ostream& os, const Capability& cap) {
  return os << CapabilityText(cap);
}

inline std::string EventText(const Event& event) {
  switch (event) {
    CASE_RETURN_TEXT(Event::PLAYBACK_STATUS_CHANGED);
    CASE_RETURN_TEXT(Event::TRACK_CHANGED);
    CASE_RETURN_TEXT(Event::PLAYBACK_POS_CHANGED);
    CASE_RETURN_TEXT(Event::PLAYER_APPLICATION_SETTING_CHANGED);
    CASE_RETURN_TEXT(Event::NOW_PLAYING_CONTENT_CHANGED);
    CASE_RETURN_TEXT(Event::AVAILABLE_PLAYERS_CHANGED);
    CASE_RETURN_TEXT(Event::ADDRESSED_PLAYER_CHANGED);
    CASE_RETURN_TEXT(Event::UIDS_CHANGED);
    CASE_RETURN_TEXT(Event::VOLUME_CHANGED);
    default:
      return "Unknown Event: " + loghex(event);
  }
}

inline std::ostream& operator<<(std::ostream& os, const Event& event) {
  return os << EventText(event);
}

inline std::string AttributeText(const Attribute& attr) {
  switch (attr) {
    CASE_RETURN_TEXT(Attribute::TITLE);
    CASE_RETURN_TEXT(Attribute::ARTIST_NAME);
    CASE_RETURN_TEXT(Attribute::ALBUM_NAME);
    CASE_RETURN_TEXT(Attribute::TRACK_NUMBER);
    CASE_RETURN_TEXT(Attribute::TOTAL_NUMBER_OF_TRACKS);
    CASE_RETURN_TEXT(Attribute::GENRE);
    CASE_RETURN_TEXT(Attribute::PLAYING_TIME);
    CASE_RETURN_TEXT(Attribute::DEFAULT_COVER_ART);
    default:
      return "Unknown Attribute Value: " + loghex(attr);
  }
}

inline std::ostream& operator<<(std::ostream& os, const Attribute& attr) {
  return os << AttributeText(attr);
}

inline std::string StatusText(const Status& status) {
  switch (status) {
    CASE_RETURN_TEXT(Status::INVALID_COMMAND);
    CASE_RETURN_TEXT(Status::INVALID_PARAMETER);
    CASE_RETURN_TEXT(Status::PARAMETER_CONTENT_ERROR);
    CASE_RETURN_TEXT(Status::INTERNAL_ERROR);
    CASE_RETURN_TEXT(Status::NO_ERROR);
    CASE_RETURN_TEXT(Status::UIDS_CHANGED);
    CASE_RETURN_TEXT(Status::RESERVED);
    CASE_RETURN_TEXT(Status::INVALID_DIRECTION);
    CASE_RETURN_TEXT(Status::NOT_A_DIRECTORY);
    CASE_RETURN_TEXT(Status::DOES_NOT_EXIST);
    CASE_RETURN_TEXT(Status::INVALID_SCOPE);
    CASE_RETURN_TEXT(Status::RANGE_OUT_OF_BOUNDS);
    CASE_RETURN_TEXT(Status::FOLDER_ITEM_NOT_PLAYABLE);
    CASE_RETURN_TEXT(Status::MEDIA_IN_USE);
    CASE_RETURN_TEXT(Status::NOW_PLAYING_LIST_FULL);
    CASE_RETURN_TEXT(Status::SEARCH_NOT_SUPPORTED);
    CASE_RETURN_TEXT(Status::SEARCH_IN_PROGRESS);
    CASE_RETURN_TEXT(Status::INVALID_PLAYER_ID);
    CASE_RETURN_TEXT(Status::PLAYER_NOT_BROWSABLE);
    CASE_RETURN_TEXT(Status::PLAYER_NOT_ADDRESSED);
    CASE_RETURN_TEXT(Status::NO_VALID_SEARCH_RESULTS);
    CASE_RETURN_TEXT(Status::NO_AVAILABLE_PLAYERS);
    CASE_RETURN_TEXT(Status::ADDRESSED_PLAYER_CHANGED);
    default:
      return "Unknown Status: " + loghex(status);
  }
}

inline std::ostream& operator<<(std::ostream& os, const Status& status) {
  return os << StatusText(status);
}

}  // namespace avrcp
}  // namespace bluetooth
 No newline at end of file
+117 −0
Original line number Diff line number Diff line
/*
 * Copyright 2018 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 <base/logging.h>
#include <iomanip>
#include <sstream>
#include <type_traits>

#include "avrcp_packet.h"

namespace bluetooth {
namespace avrcp {

std::unique_ptr<PacketBuilder> PacketBuilder::MakeBuilder(
    CType type, uint8_t subunit_type, uint8_t subunit_id, Opcode opcode,
    std::unique_ptr<::bluetooth::PacketBuilder> payload) {
  std::unique_ptr<PacketBuilder> builder = std::unique_ptr<PacketBuilder>(
      new PacketBuilder(type, subunit_type, subunit_id, opcode));

  builder->payload_ = std::move(payload);

  return builder;
}

size_t PacketBuilder::size() const {
  // The size of the header for an Packet is 3
  return payload_->size() + Packet::kHeaderSize();
}

bool PacketBuilder::Serialize(const std::shared_ptr<::bluetooth::Packet>& pkt) {
  ReserveSpace(pkt, size());

  // Push the header for the packet
  PushHeader(pkt);

  // Push the payload for the packet
  return payload_->Serialize(pkt);
}

void PacketBuilder::PushHeader(
    const std::shared_ptr<::bluetooth::Packet>& pkt) {
  AddPayloadOctets1(pkt, static_cast<uint8_t>(c_type_));
  AddPayloadOctets1(pkt, (subunit_type_ << 3) | subunit_id_);
  AddPayloadOctets1(pkt, static_cast<uint8_t>(opcode_));
}

bool PacketBuilder::PushCompanyId(
    const std::shared_ptr<::bluetooth::Packet>& pkt, uint32_t company_id) {
  company_id = base::ByteSwap(company_id);
  for (int i = 0; i < 3; i++) {
    company_id >>= 8;
    AddPayloadOctets1(pkt, company_id & 0xFF);
  }

  return true;
}

std::shared_ptr<Packet> Packet::Parse(
    std::shared_ptr<::bluetooth::Packet> pkt) {
  return std::shared_ptr<Packet>(new Packet(pkt));
}

CType Packet::GetCType() const {
  auto value = *begin() & 0x0F;
  return static_cast<CType>(value);
}

uint8_t Packet::GetSubunitType() const {
  return *(begin() + static_cast<size_t>(1)) >> 3;
}

uint8_t Packet::GetSubunitId() const {
  return *(begin() + static_cast<size_t>(1)) & 0b00000111;
}

Opcode Packet::GetOpcode() const {
  auto value = *(begin() + static_cast<size_t>(2));
  return static_cast<Opcode>(value);
}

bool Packet::IsValid() const { return size() >= kHeaderSize(); }

std::string Packet::ToString() const {
  std::stringstream ss;
  ss << "avrcp::Packet: " << std::endl;
  ss << "  └ cType = " << GetCType() << std::endl;
  ss << "  └ Subunit Type = " << loghex(GetSubunitType()) << std::endl;
  ss << "  └ Subunit ID = " << loghex(GetSubunitId()) << std::endl;
  ss << "  └ OpCode = " << GetOpcode() << std::endl;
  ss << "  └ Payload =";
  for (auto it = begin() + static_cast<size_t>(3); it != end(); it++) {
    ss << " " << loghex(*it);
  }
  ss << std::endl;

  return ss.str();
}

std::pair<size_t, size_t> Packet::GetPayloadIndecies() const {
  return std::pair<size_t, size_t>(packet_start_index_ + 3, packet_end_index_);
}

}  // namespace avrcp
}  // namespace bluetooth
 No newline at end of file
Loading