Loading system/packet/Android.bp +15 −6 Original line number Diff line number Diff line Loading @@ -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", ], } system/packet/avrcp/Android.bp +9 −2 Original line number Diff line number Diff line Loading @@ -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", Loading system/packet/avrcp/avrcp_browse_packet.cc 0 → 100644 +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 system/packet/avrcp/avrcp_browse_packet.h 0 → 100644 +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 system/packet/avrcp/avrcp_common.h +198 −29 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ #include <base/sys_byteorder.h> #include <map> // This file contains the different AVRCP Constants namespace bluetooth { namespace avrcp { Loading Loading @@ -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 { Loading @@ -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
system/packet/Android.bp +15 −6 Original line number Diff line number Diff line Loading @@ -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", ], }
system/packet/avrcp/Android.bp +9 −2 Original line number Diff line number Diff line Loading @@ -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", Loading
system/packet/avrcp/avrcp_browse_packet.cc 0 → 100644 +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
system/packet/avrcp/avrcp_browse_packet.h 0 → 100644 +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
system/packet/avrcp/avrcp_common.h +198 −29 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ #include <base/sys_byteorder.h> #include <map> // This file contains the different AVRCP Constants namespace bluetooth { namespace avrcp { Loading Loading @@ -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 { Loading @@ -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