You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

346 lines
11 KiB

/*
* 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>
GetFolderItemsResponseBuilder::MakePlayerListBuilder(Status status,
uint16_t uid_counter,
size_t mtu) {
std::unique_ptr<GetFolderItemsResponseBuilder> builder(
new GetFolderItemsResponseBuilder(Scope::MEDIA_PLAYER_LIST, status,
uid_counter, mtu));
return builder;
}
std::unique_ptr<GetFolderItemsResponseBuilder>
GetFolderItemsResponseBuilder::MakeVFSBuilder(Status status,
uint16_t uid_counter,
size_t mtu) {
std::unique_ptr<GetFolderItemsResponseBuilder> builder(
new GetFolderItemsResponseBuilder(Scope::VFS, status, uid_counter, mtu));
return builder;
}
std::unique_ptr<GetFolderItemsResponseBuilder>
GetFolderItemsResponseBuilder::MakeNowPlayingBuilder(Status status,
uint16_t uid_counter,
size_t mtu) {
std::unique_ptr<GetFolderItemsResponseBuilder> 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<Scope>(*it);
}
uint32_t GetFolderItemsRequest::GetStartItem() const {
auto it = begin() + BrowsePacket::kMinSize() + static_cast<size_t>(1);
return it.extractBE<uint32_t>();
}
uint32_t GetFolderItemsRequest::GetEndItem() const {
auto it = begin() + BrowsePacket::kMinSize() + static_cast<size_t>(5);
return it.extractBE<uint32_t>();
}
uint8_t GetFolderItemsRequest::GetNumAttributes() const {
auto it = begin() + BrowsePacket::kMinSize() + static_cast<size_t>(9);
return *it;
}
std::vector<Attribute> GetFolderItemsRequest::GetAttributesRequested() const {
auto it = begin() + BrowsePacket::kMinSize() + static_cast<size_t>(9);
size_t number_of_attributes = it.extract<uint8_t>();
std::vector<Attribute> 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<uint32_t>());
}
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>
GetFolderItemsRequestBuilder::MakeBuilder(
Scope scope, uint32_t start_item, uint32_t end_item,
const std::set<Attribute>& requested_attrs) {
std::unique_ptr<GetFolderItemsRequestBuilder> 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<uint8_t>(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<uint32_t>(attr)));
}
return true;
}
} // namespace avrcp
} // namespace bluetooth