/* * 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 "get_folder_items.h" namespace bluetooth { namespace avrcp { std::unique_ptr GetFolderItemsResponseBuilder::MakePlayerListBuilder(Status status, uint16_t uid_counter, size_t mtu) { std::unique_ptr builder( new GetFolderItemsResponseBuilder(Scope::MEDIA_PLAYER_LIST, status, uid_counter, mtu)); return builder; } std::unique_ptr GetFolderItemsResponseBuilder::MakeVFSBuilder(Status status, uint16_t uid_counter, size_t mtu) { std::unique_ptr builder( new GetFolderItemsResponseBuilder(Scope::VFS, status, uid_counter, mtu)); return builder; } std::unique_ptr GetFolderItemsResponseBuilder::MakeNowPlayingBuilder(Status status, uint16_t uid_counter, size_t mtu) { std::unique_ptr builder( new GetFolderItemsResponseBuilder(Scope::NOW_PLAYING, status, uid_counter, mtu)); return builder; } size_t GetFolderItemsResponseBuilder::size() const { size_t len = BrowsePacket::kMinSize(); len += 1; // Status // There is nothing other than the status in the packet if the status isn't // NO_ERROR if (status_ != Status::NO_ERROR || items_.size() == 0) return len; len += 2; // UID Counter len += 2; // Number of Items; for (const auto& item : items_) { len += item.size(); } return len; } bool GetFolderItemsResponseBuilder::Serialize( const std::shared_ptr<::bluetooth::Packet>& pkt) { ReserveSpace(pkt, size()); BrowsePacketBuilder::PushHeader(pkt, size() - BrowsePacket::kMinSize()); if (status_ == Status::NO_ERROR && items_.size() == 0) { // Return range out of bounds if there are zero items in the folder status_ = Status::RANGE_OUT_OF_BOUNDS; } AddPayloadOctets1(pkt, (uint8_t)status_); // Status if (status_ != Status::NO_ERROR) return true; AddPayloadOctets2(pkt, base::ByteSwap(uid_counter_)); uint16_t num_items = items_.size(); AddPayloadOctets2(pkt, base::ByteSwap(num_items)); for (const auto& item : items_) { PushMediaListItem(pkt, item); } return true; } bool GetFolderItemsResponseBuilder::AddMediaPlayer(MediaPlayerItem item) { CHECK(scope_ == Scope::MEDIA_PLAYER_LIST); if (size() + item.size() > mtu_) return false; items_.push_back(MediaListItem(item)); return true; } bool GetFolderItemsResponseBuilder::AddSong(MediaElementItem item) { CHECK(scope_ == Scope::VFS || scope_ == Scope::NOW_PLAYING); if (size() + item.size() > mtu_) return false; items_.push_back(MediaListItem(item)); return true; } bool GetFolderItemsResponseBuilder::AddFolder(FolderItem item) { CHECK(scope_ == Scope::VFS); if (size() + item.size() > mtu_) return false; items_.push_back(MediaListItem(item)); return true; } void GetFolderItemsResponseBuilder::PushMediaListItem( const std::shared_ptr<::bluetooth::Packet>& pkt, const MediaListItem& item) { switch (item.type_) { case MediaListItem::PLAYER: PushMediaPlayerItem(pkt, item.player_); break; case MediaListItem::FOLDER: PushFolderItem(pkt, item.folder_); break; case MediaListItem::SONG: PushMediaElementItem(pkt, item.song_); break; } } void GetFolderItemsResponseBuilder::PushMediaPlayerItem( const std::shared_ptr<::bluetooth::Packet>& pkt, const MediaPlayerItem& item) { AddPayloadOctets1(pkt, 0x01); // Media Player Item uint16_t item_len = item.size() - 3; AddPayloadOctets2(pkt, base::ByteSwap(item_len)); // Item length AddPayloadOctets2(pkt, base::ByteSwap(item.id_)); // Player ID AddPayloadOctets1(pkt, 0x01); // Player Type AddPayloadOctets4(pkt, 0x00000000); // Player Subtype AddPayloadOctets1( pkt, 0x02); // Player Play Status // TODO: Add this as a passed field // Features AddPayloadOctets1(pkt, 0x00); AddPayloadOctets1(pkt, 0x00); AddPayloadOctets1(pkt, 0x00); AddPayloadOctets1(pkt, 0x00); AddPayloadOctets1(pkt, 0x00); AddPayloadOctets1(pkt, 0xb7); AddPayloadOctets1(pkt, 0x01); if (item.browsable_) { AddPayloadOctets1(pkt, 0x0C); AddPayloadOctets1(pkt, 0x0a); } else { AddPayloadOctets1(pkt, 0x04); AddPayloadOctets1(pkt, 0x00); } AddPayloadOctets1(pkt, 0x00); AddPayloadOctets1(pkt, 0x00); AddPayloadOctets1(pkt, 0x00); AddPayloadOctets1(pkt, 0x00); AddPayloadOctets1(pkt, 0x00); AddPayloadOctets1(pkt, 0x00); AddPayloadOctets1(pkt, 0x00); AddPayloadOctets2(pkt, base::ByteSwap((uint16_t)0x006a)); uint16_t name_len = item.name_.size(); AddPayloadOctets2(pkt, base::ByteSwap(name_len)); for (const uint8_t& byte : item.name_) { AddPayloadOctets1(pkt, byte); } } void GetFolderItemsResponseBuilder::PushFolderItem( const std::shared_ptr<::bluetooth::Packet>& pkt, const FolderItem& item) { AddPayloadOctets1(pkt, 0x02); // Folder Item uint16_t item_len = item.size() - 3; AddPayloadOctets2(pkt, base::ByteSwap(item_len)); AddPayloadOctets8(pkt, base::ByteSwap(item.uid_)); AddPayloadOctets1(pkt, item.folder_type_); AddPayloadOctets1(pkt, item.is_playable_ ? 0x01 : 0x00); AddPayloadOctets2(pkt, base::ByteSwap((uint16_t)0x006a)); // UTF-8 Character Set uint16_t name_len = item.name_.size(); AddPayloadOctets2(pkt, base::ByteSwap(name_len)); for (const uint8_t& byte : item.name_) { AddPayloadOctets1(pkt, byte); } } void GetFolderItemsResponseBuilder::PushMediaElementItem( const std::shared_ptr<::bluetooth::Packet>& pkt, const MediaElementItem& item) { AddPayloadOctets1(pkt, 0x03); // Media Element Item uint16_t item_len = item.size() - 3; AddPayloadOctets2(pkt, base::ByteSwap(item_len)); AddPayloadOctets8(pkt, base::ByteSwap(item.uid_)); AddPayloadOctets1(pkt, 0x00); // Media Type Audio AddPayloadOctets2(pkt, base::ByteSwap((uint16_t)0x006a)); // UTF-8 Character Set uint16_t name_len = item.name_.size(); AddPayloadOctets2(pkt, base::ByteSwap(name_len)); for (const uint8_t& byte : item.name_) { AddPayloadOctets1(pkt, byte); } AddPayloadOctets1(pkt, (uint8_t)item.attributes_.size()); for (const auto& entry : item.attributes_) { AddPayloadOctets4(pkt, base::ByteSwap((uint32_t)entry.attribute())); AddPayloadOctets2(pkt, base::ByteSwap((uint16_t)0x006a)); // UTF-8 Character Set std::string attr_val = entry.value(); uint16_t attr_len = attr_val.size(); AddPayloadOctets2(pkt, base::ByteSwap(attr_len)); for (const uint8_t& byte : attr_val) { AddPayloadOctets1(pkt, byte); } } } Scope GetFolderItemsRequest::GetScope() const { auto it = begin() + BrowsePacket::kMinSize(); return static_cast(*it); } uint32_t GetFolderItemsRequest::GetStartItem() const { auto it = begin() + BrowsePacket::kMinSize() + static_cast(1); return it.extractBE(); } uint32_t GetFolderItemsRequest::GetEndItem() const { auto it = begin() + BrowsePacket::kMinSize() + static_cast(5); return it.extractBE(); } uint8_t GetFolderItemsRequest::GetNumAttributes() const { auto it = begin() + BrowsePacket::kMinSize() + static_cast(9); return *it; } std::vector GetFolderItemsRequest::GetAttributesRequested() const { auto it = begin() + BrowsePacket::kMinSize() + static_cast(9); size_t number_of_attributes = it.extract(); std::vector attribute_list; // No attributes requested if (number_of_attributes == 0xFF) return attribute_list; // TODO: If the number of attributes equals 0, then all attributes are // requested right now thats handled in the service itself, but it'd be nice // to have this function return a vector with all the attributes for (size_t i = 0; i < number_of_attributes; i++) { attribute_list.push_back((Attribute)it.extractBE()); } return attribute_list; } bool GetFolderItemsRequest::IsValid() const { if (!BrowsePacket::IsValid()) return false; // The minimum size required to be valid if (size() < kMinSize()) return false; auto attr_count = GetNumAttributes(); // No items requested if (attr_count == 0xFF) return true; auto attr_start = begin() + kMinSize(); // Casting the int returned from end - attr_start should be fine. If an // overflow occurs we can definitly say the packet is invalid return (attr_count * sizeof(Attribute)) == (size_t)(end() - attr_start); } std::string GetFolderItemsRequest::ToString() const { std::stringstream ss; ss << "GetFolderItemsRequestPacket: " << std::endl; ss << " └ PDU = " << GetPdu() << std::endl; ss << " └ Length = " << GetLength() << std::endl; ss << " └ Scope = " << GetScope() << std::endl; ss << " └ Start Item = " << loghex(GetStartItem()) << std::endl; ss << " └ End Item = " << loghex(GetEndItem()) << std::endl; ss << " └ Attribute Count = " << loghex(GetNumAttributes()) << std::endl; ss << std::endl; return ss.str(); } std::unique_ptr GetFolderItemsRequestBuilder::MakeBuilder( Scope scope, uint32_t start_item, uint32_t end_item, const std::set& requested_attrs) { std::unique_ptr builder( new GetFolderItemsRequestBuilder(scope, start_item, end_item, requested_attrs)); return builder; } size_t GetFolderItemsRequestBuilder::size() const { size_t len = GetFolderItemsRequest::kMinSize(); len += requested_attrs_.size() * sizeof(Attribute); return len; } bool GetFolderItemsRequestBuilder::Serialize( const std::shared_ptr<::bluetooth::Packet>& pkt) { ReserveSpace(pkt, size()); BrowsePacketBuilder::PushHeader(pkt, size() - BrowsePacket::kMinSize()); AddPayloadOctets1(pkt, static_cast(scope_)); AddPayloadOctets4(pkt, base::ByteSwap(start_item_)); AddPayloadOctets4(pkt, base::ByteSwap(end_item_)); if (requested_attrs_.size() == 0) { // 0xFF is the value to signify that there are no attributes requested. AddPayloadOctets1(pkt, 0xFF); return true; } AddPayloadOctets1(pkt, requested_attrs_.size()); for (const auto& attr : requested_attrs_) { AddPayloadOctets4(pkt, base::ByteSwap(static_cast(attr))); } return true; } } // namespace avrcp } // namespace bluetooth