普通文本  |  346行  |  11.18 KB

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