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

Commit 8cbbbd14 authored by Ajay Panicker's avatar Ajay Panicker
Browse files

Add AVRCP Browse packets to the completed packets.

Also add class representations for the following packet types:
  AVRCP Browse Packet
    AVRCP Change Path
    AVRCP Get Folder Items
    AVRCP Get Item Attributes
    AVRCP Set Browsed Player
    AVRCP Get Total Number of Items
    AVRCP Play Item

Bug: 68854188
Test: run host native test net_test_packets
Change-Id: I0bc682b11ec9181cfe1ed92678bb869ff3ef5740
parent f671ee8f
Loading
Loading
Loading
Loading
+15 −6
Original line number Diff line number Diff line
@@ -18,21 +18,30 @@ cc_test {
    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_browse_packet_test.cc",
        "tests/avrcp/avrcp_packet_test.cc",
        "tests/avrcp/vendor_packet_test.cc",
        "tests/avrcp/avrcp_reject_packet_test.cc",
        "tests/avrcp/change_path_packet_test.cc",
        "tests/avrcp/get_capabilities_packet_test.cc",
        "tests/avrcp/get_element_attributes_packet_test.cc",
        "tests/avrcp/get_folder_items_packet_test.cc",
        "tests/avrcp/get_item_attributes_packet_test.cc",
        "tests/avrcp/get_play_status_packet_test.cc",
        "tests/avrcp/get_total_number_of_items_packet_test.cc",
        "tests/avrcp/pass_through_packet_test.cc",
        "tests/avrcp/play_item_packet_test.cc",
        "tests/avrcp/register_notification_packet_test.cc",
        "tests/avrcp/avrcp_reject_packet_test.cc",
        "tests/avrcp/set_browsed_player_packet_test.cc",
        "tests/avrcp/vendor_packet_test.cc",
        "tests/base/iterator_test.cc",
        "tests/base/packet_builder_test.cc",
        "tests/base/packet_test.cc",
    ],
    static_libs: [
        "libgmock",
        "lib-bt-packets",
    ],
    cflags: ["-DBUILDCFG","-g"],
    cflags: [
        "-DBUILDCFG",
    ],
}
+9 −2
Original line number Diff line number Diff line
@@ -4,14 +4,21 @@ cc_library_static {
    export_include_dirs: ["."],
    host_supported: true,
    srcs: [
        "avrcp_browse_packet.cc",
        "avrcp_packet.cc",
        "vendor_packet.cc",
        "avrcp_reject_packet.cc",
        "capabilities_packet.cc",
        "change_path.cc",
        "get_element_attributes_packet.cc",
        "get_folder_items.cc",
        "get_item_attributes.cc",
        "get_play_status_packet.cc",
        "get_total_number_of_items.cc",
        "pass_through_packet.cc",
        "play_item.cc",
        "register_notification_packet.cc",
        "avrcp_reject_packet.cc",
        "set_browsed_player.cc",
        "vendor_packet.cc",
    ],
    static_libs: [
        "lib-bt-packets-base",
+91 −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 "avrcp_browse_packet.h"

#include <base/logging.h>

namespace bluetooth {
namespace avrcp {

std::unique_ptr<BrowsePacketBuilder> BrowsePacketBuilder::MakeBuilder(
    BrowsePdu pdu, std::unique_ptr<::bluetooth::PacketBuilder> payload) {
  std::unique_ptr<BrowsePacketBuilder> builder =
      std::unique_ptr<BrowsePacketBuilder>(new BrowsePacketBuilder(pdu));

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

  return builder;
}

size_t BrowsePacketBuilder::size() const {
  return BrowsePacket::kMinSize() + payload_->size();
}

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

  PushHeader(pkt, payload_->size());

  return payload_->Serialize(pkt);
}

void BrowsePacketBuilder::PushHeader(
    const std::shared_ptr<::bluetooth::Packet>& pkt, uint16_t length) {
  AddPayloadOctets1(pkt, (uint8_t)pdu_);
  AddPayloadOctets2(pkt, base::ByteSwap(length));
}

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

BrowsePdu BrowsePacket::GetPdu() const {
  return static_cast<BrowsePdu>(*begin());
}

uint16_t BrowsePacket::GetLength() const {
  auto it = begin() + static_cast<size_t>(1);
  return base::ByteSwap(it.extract<uint16_t>());
}

bool BrowsePacket::IsValid() const {
  if (size() < kMinSize()) return false;
  return size() == GetLength() + kMinSize();
}

std::string BrowsePacket::ToString() const {
  std::stringstream ss;
  ss << "AvrcpBrowsePacket: " << std::endl;
  ss << "  └ PDU = " << GetPdu() << std::endl;
  ss << "  └ Length = " << GetLength() << 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> BrowsePacket::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
+84 −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/logging.h>
#include <base/macros.h>
#include <iostream>

#include "iterator.h"
#include "packet.h"
#include "packet_builder.h"

#include "avrcp_common.h"
#include "avrcp_logging_helper.h"

namespace bluetooth {
namespace avrcp {

class BrowsePacketBuilder : public ::bluetooth::PacketBuilder {
 public:
  virtual ~BrowsePacketBuilder() = default;

  static std::unique_ptr<BrowsePacketBuilder> MakeBuilder(
      BrowsePdu pdu, std::unique_ptr<::bluetooth::PacketBuilder> payload);

  virtual size_t size() const override;
  virtual bool Serialize(
      const std::shared_ptr<::bluetooth::Packet>& pkt) override;

 protected:
  BrowsePdu pdu_;
  std::unique_ptr<::bluetooth::PacketBuilder> payload_;

  void PushHeader(const std::shared_ptr<::bluetooth::Packet>& pkt,
                  uint16_t length);

  BrowsePacketBuilder(BrowsePdu pdu) : pdu_(pdu){};
};

class BrowsePacket : public ::bluetooth::Packet {
 public:
  virtual ~BrowsePacket() = default;

  static std::shared_ptr<BrowsePacket> Parse(
      std::shared_ptr<::bluetooth::Packet> pkt);

  /**
   * Avrcp Browse Packet Layout
   *   uint8_t pdu_;
   *   uint16_t length_;
   *   uint8_t[] payload_;
   */
  static constexpr size_t kMinSize() { return 3; }

  BrowsePdu GetPdu() const;
  uint16_t GetLength() const;

  virtual bool IsValid() const override;
  virtual std::string ToString() const override;

 protected:
  using ::bluetooth::Packet::Packet;

 private:
  virtual std::pair<size_t, size_t> GetPayloadIndecies() const;
  DISALLOW_COPY_AND_ASSIGN(BrowsePacket);
};

}  // namespace avrcp
}  // namespace bluetooth
 No newline at end of file
+198 −29
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@

#include <base/sys_byteorder.h>

#include <map>

// This file contains the different AVRCP Constants
namespace bluetooth {
namespace avrcp {
@@ -50,6 +52,8 @@ enum class CommandPdu : uint8_t {
  GET_ELEMENT_ATTRIBUTES = 0x20,
  GET_PLAY_STATUS = 0x30,
  REGISTER_NOTIFICATION = 0x31,
  SET_ADDRESSED_PLAYER = 0x60,
  PLAY_ITEM = 0x74,
};

enum class PacketType : uint8_t {
@@ -76,42 +80,207 @@ enum class Event : uint8_t {

enum class Attribute : uint32_t {
  TITLE = 0x01,
  ARTIST_NAME,
  ALBUM_NAME,
  TRACK_NUMBER,
  TOTAL_NUMBER_OF_TRACKS,
  GENRE,
  PLAYING_TIME,
  DEFAULT_COVER_ART,
  ARTIST_NAME = 0x02,
  ALBUM_NAME = 0x03,
  TRACK_NUMBER = 0x04,
  TOTAL_NUMBER_OF_TRACKS = 0x05,
  GENRE = 0x06,
  PLAYING_TIME = 0x07,
  DEFAULT_COVER_ART = 0x08,
};

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,
  INVALID_PARAMETER = 0x01,
  PARAMETER_CONTENT_ERROR = 0x02,
  INTERNAL_ERROR = 0x03,
  NO_ERROR = 0x04,
  UIDS_CHANGED = 0x05,
  RESERVED = 0x06,
  INVALID_DIRECTION = 0x07,
  NOT_A_DIRECTORY = 0x08,
  DOES_NOT_EXIST = 0x09,
  INVALID_SCOPE = 0x0a,
  RANGE_OUT_OF_BOUNDS = 0xb,
  FOLDER_ITEM_NOT_PLAYABLE = 0x0c,
  MEDIA_IN_USE = 0x0d,
  NOW_PLAYING_LIST_FULL = 0x0e,
  SEARCH_NOT_SUPPORTED = 0x0f,
  SEARCH_IN_PROGRESS = 0x10,
  INVALID_PLAYER_ID = 0x11,
  PLAYER_NOT_BROWSABLE = 0x12,
  PLAYER_NOT_ADDRESSED = 0x13,
  NO_VALID_SEARCH_RESULTS = 0x14,
  NO_AVAILABLE_PLAYERS = 0x15,
  ADDRESSED_PLAYER_CHANGED = 0x16,
};

enum class BrowsePdu : uint8_t {
  SET_BROWSED_PLAYER = 0x70,
  GET_FOLDER_ITEMS = 0x71,
  CHANGE_PATH = 0x72,
  GET_ITEM_ATTRIBUTES = 0x73,
  GET_TOTAL_NUMBER_OF_ITEMS = 0x75,
};

enum class Scope : uint8_t {
  MEDIA_PLAYER_LIST = 0x00,
  VFS = 0x01,
  SEARCH = 0x02,
  NOW_PLAYING = 0x03,
};

enum class Direction : uint8_t {
  UP = 0x00,
  DOWN = 0x01,
};

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

struct MediaPlayerItem {
  uint16_t id_;
  std::string name_;
  bool browsable_;

  MediaPlayerItem(uint16_t id, std::string name, bool browsable)
      : id_(id), name_(name), browsable_(browsable) {}

  MediaPlayerItem(const MediaPlayerItem&) = default;

  static size_t size(const MediaPlayerItem& item) {
    size_t ret = 0;
    ret += 1;   // Media Player Type
    ret += 2;   // Item Length
    ret += 2;   // Player Id
    ret += 1;   // Player Type
    ret += 4;   // Player Subtype
    ret += 1;   // Play Status
    ret += 16;  // Features
    ret += 2;   // UTF-8 character set
    ret += 2;   // Name Length
    ret += item.name_.size();
    return ret;
  }
};

struct FolderItem {
  uint64_t uid_;
  uint8_t folder_type_;
  bool is_playable_;
  std::string name_;

  FolderItem(uint64_t uid, uint8_t folder_type, bool is_playable,
             const std::string& name)
      : uid_(uid),
        folder_type_(folder_type),
        is_playable_(is_playable),
        name_(name) {}

  FolderItem(const FolderItem&) = default;

  static size_t size(const FolderItem& item) {
    size_t ret = 0;
    ret += 1;  // Folder Item Type
    ret += 2;  // Item Length
    ret += 8;  // Folder UID
    ret += 1;  // Folder Type
    ret += 1;  // Is Playable byte
    ret += 2;  // UTF-8 Character Set
    ret += 2;  // Name Length
    ret += item.name_.size();
    return ret;
  }
};

// NOTE: We never use media type field because we only support audio types
struct MediaElementItem {
  uint64_t uid_ = 0;
  std::string name_;
  std::map<Attribute, std::string> attributes_;

  MediaElementItem(uint64_t uid, std::string name,
                   std::map<Attribute, std::string> attributes)
      : uid_(uid), name_(name), attributes_(attributes) {}

  MediaElementItem(const MediaElementItem&) = default;

  static size_t size(const MediaElementItem& item) {
    size_t ret = 0;
    ret += 1;  // Media Element Item Type
    ret += 2;  // Item Length
    ret += 8;  // Item UID
    ret += 1;  // Media Type
    ret += 2;  // UTF-8 Character Set
    ret += 2;  // Name Length
    ret += item.name_.size();
    ret += 1;  // Number of Attributes
    for (auto it = item.attributes_.begin(); it != item.attributes_.end();
         it++) {
      ret += 4;  // Attribute ID
      ret += 2;  // UTF-8 Character Set
      ret += 2;  // Attribute Length
      ret += it->second.size();
    }

    return ret;
  }
};

struct MediaListItem {
  enum : uint8_t { PLAYER = 0x01, FOLDER = 0x02, SONG = 0x03 } type_;

  union {
    MediaPlayerItem player_;
    FolderItem folder_;
    MediaElementItem song_;
  };

  MediaListItem(MediaPlayerItem item) : type_(PLAYER), player_(item) {}

  MediaListItem(FolderItem item) : type_(FOLDER), folder_(item) {}

  MediaListItem(MediaElementItem item) : type_(SONG), song_(item) {}

  MediaListItem(const MediaListItem& item) {
    type_ = item.type_;
    switch (item.type_) {
      case PLAYER:
        new (&player_) MediaPlayerItem(item.player_);
        return;
      case FOLDER:
        new (&folder_) FolderItem(item.folder_);
        return;
      case SONG:
        new (&song_) MediaElementItem(item.song_);
        return;
    }
  }

  ~MediaListItem() {
    switch (type_) {
      case PLAYER:
        player_.~MediaPlayerItem();
        return;
      case FOLDER:
        folder_.~FolderItem();
        return;
      case SONG:
        song_.~MediaElementItem();
        return;
    }
  }

  static size_t size(const MediaListItem& item) {
    switch (item.type_) {
      case PLAYER:
        return MediaPlayerItem::size(item.player_);
      case FOLDER:
        return FolderItem::size(item.folder_);
      case SONG:
        return MediaElementItem::size(item.song_);
    }
  }
};

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