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
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
|