355 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			355 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			C++
		
	
	
	
/*
 | 
						|
 * 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 <set>
 | 
						|
 | 
						|
#include <base/sys_byteorder.h>
 | 
						|
 | 
						|
namespace bluetooth {
 | 
						|
namespace avrcp {
 | 
						|
 | 
						|
constexpr uint32_t BLUETOOTH_COMPANY_ID = 0x001958;
 | 
						|
 | 
						|
constexpr uint8_t MAX_TRANSACTION_LABEL = 0xF;
 | 
						|
 | 
						|
enum class CType : uint8_t {
 | 
						|
  CONTROL = 0x0,
 | 
						|
  STATUS = 0x1,
 | 
						|
  NOTIFY = 0x3,
 | 
						|
  NOT_IMPLEMENTED = 0x8,
 | 
						|
  ACCEPTED = 0x9,
 | 
						|
  REJECTED = 0xa,
 | 
						|
  STABLE = 0xc,
 | 
						|
  CHANGED = 0xd,
 | 
						|
  INTERIM = 0xf,
 | 
						|
};
 | 
						|
 | 
						|
enum class Opcode : uint8_t {
 | 
						|
  VENDOR = 0x00,
 | 
						|
  UNIT_INFO = 0x30,
 | 
						|
  SUBUNIT_INFO = 0x31,
 | 
						|
  PASS_THROUGH = 0x7c,
 | 
						|
};
 | 
						|
 | 
						|
// Found in AVRCP_v1.6.1 Section 4.5 Table 4.5
 | 
						|
// Searching can be done in the spec by Camel Casing the constant name
 | 
						|
enum class CommandPdu : uint8_t {
 | 
						|
  GET_CAPABILITIES = 0x10,
 | 
						|
  LIST_APPLICATION_SETTING_ATTRIBUTES = 0x11,
 | 
						|
  GET_ELEMENT_ATTRIBUTES = 0x20,
 | 
						|
  GET_PLAY_STATUS = 0x30,
 | 
						|
  REGISTER_NOTIFICATION = 0x31,
 | 
						|
  SET_ABSOLUTE_VOLUME = 0x50,
 | 
						|
  SET_ADDRESSED_PLAYER = 0x60,
 | 
						|
  PLAY_ITEM = 0x74,
 | 
						|
};
 | 
						|
 | 
						|
enum class PacketType : uint8_t {
 | 
						|
  SINGLE = 0x00,
 | 
						|
};
 | 
						|
 | 
						|
enum class Capability : uint8_t {
 | 
						|
  COMPANY_ID = 0x02,
 | 
						|
  EVENTS_SUPPORTED = 0x03,
 | 
						|
};
 | 
						|
 | 
						|
// Found in AVRCP_v1.6.1 Section 28 Appendix H
 | 
						|
enum class Event : uint8_t {
 | 
						|
  PLAYBACK_STATUS_CHANGED = 0x01,
 | 
						|
  TRACK_CHANGED = 0x02,
 | 
						|
  PLAYBACK_POS_CHANGED = 0x05,
 | 
						|
  PLAYER_APPLICATION_SETTING_CHANGED = 0x08,
 | 
						|
  NOW_PLAYING_CONTENT_CHANGED = 0x09,
 | 
						|
  AVAILABLE_PLAYERS_CHANGED = 0x0a,
 | 
						|
  ADDRESSED_PLAYER_CHANGED = 0x0b,
 | 
						|
  UIDS_CHANGED = 0x0c,
 | 
						|
  VOLUME_CHANGED = 0x0d,
 | 
						|
};
 | 
						|
 | 
						|
enum class Attribute : uint32_t {
 | 
						|
  TITLE = 0x01,
 | 
						|
  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 = 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,
 | 
						|
  GENERAL_REJECT = 0xa0,
 | 
						|
};
 | 
						|
 | 
						|
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,
 | 
						|
};
 | 
						|
 | 
						|
enum class KeyState : uint8_t {
 | 
						|
  PUSHED = 0x00,
 | 
						|
  RELEASED = 0x01,
 | 
						|
};
 | 
						|
 | 
						|
class AttributeEntry {
 | 
						|
 public:
 | 
						|
  AttributeEntry(const Attribute& attribute, const std::string& value)
 | 
						|
      : attribute_(attribute), value_(value) {}
 | 
						|
 | 
						|
  AttributeEntry(const Attribute& attribute) : attribute_(attribute) {}
 | 
						|
 | 
						|
  AttributeEntry(const AttributeEntry&) = default;
 | 
						|
 | 
						|
  Attribute attribute() const { return attribute_; }
 | 
						|
 | 
						|
  std::string value() const { return value_; }
 | 
						|
 | 
						|
  static constexpr size_t kHeaderSize() {
 | 
						|
    size_t ret = 0;
 | 
						|
    ret += 4;  // Size of attribute field
 | 
						|
    ret += 2;  // Size of length field
 | 
						|
    ret += 2;  // Size of character encoding field
 | 
						|
    return ret;
 | 
						|
  }
 | 
						|
 | 
						|
  size_t size() const { return kHeaderSize() + value_.size(); }
 | 
						|
 | 
						|
  void resize(size_t new_size) {
 | 
						|
    new_size = new_size < kHeaderSize() ? 0 : new_size - kHeaderSize();
 | 
						|
    if (value_.size() > new_size) {
 | 
						|
      value_.resize(new_size);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  bool empty() { return value_.empty(); }
 | 
						|
 | 
						|
  bool operator<(const AttributeEntry& rhs) const {
 | 
						|
    return attribute_ < rhs.attribute_;
 | 
						|
  }
 | 
						|
 | 
						|
 private:
 | 
						|
  Attribute attribute_;
 | 
						|
  std::string value_;
 | 
						|
};
 | 
						|
 | 
						|
constexpr size_t MAX_FIELD_LEN = 100;
 | 
						|
 | 
						|
struct MediaPlayerItem {
 | 
						|
  uint16_t id_;
 | 
						|
  std::string name_;
 | 
						|
  bool browsable_;
 | 
						|
 | 
						|
  MediaPlayerItem(uint16_t id, const std::string& name, bool browsable)
 | 
						|
      : id_(id), name_(name), browsable_(browsable) {
 | 
						|
    if (name_.size() > MAX_FIELD_LEN) {
 | 
						|
      name_.resize(MAX_FIELD_LEN);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  MediaPlayerItem(const MediaPlayerItem&) = default;
 | 
						|
 | 
						|
  static constexpr size_t kHeaderSize() {
 | 
						|
    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
 | 
						|
    return ret;
 | 
						|
  }
 | 
						|
 | 
						|
  size_t size() const { return kHeaderSize() + name_.size(); }
 | 
						|
};
 | 
						|
 | 
						|
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) {
 | 
						|
    if (name_.size() > MAX_FIELD_LEN) {
 | 
						|
      name_.resize(MAX_FIELD_LEN);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  FolderItem(const FolderItem&) = default;
 | 
						|
 | 
						|
  static constexpr size_t kHeaderSize() {
 | 
						|
    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
 | 
						|
    return ret;
 | 
						|
  }
 | 
						|
 | 
						|
  size_t size() const { return kHeaderSize() + name_.size(); }
 | 
						|
};
 | 
						|
 | 
						|
// NOTE: We never use media type field because we only support audio types
 | 
						|
struct MediaElementItem {
 | 
						|
  uint64_t uid_ = 0;
 | 
						|
  std::string name_;
 | 
						|
  std::set<AttributeEntry> attributes_;
 | 
						|
 | 
						|
  // Truncate the name and attribute fields so that we don't have a single item
 | 
						|
  // that can exceed the Browsing MTU
 | 
						|
  MediaElementItem(uint64_t uid, const std::string& name,
 | 
						|
                   std::set<AttributeEntry> attributes)
 | 
						|
      : uid_(uid), name_(name) {
 | 
						|
    if (name_.size() > MAX_FIELD_LEN) {
 | 
						|
      name_.resize(MAX_FIELD_LEN);
 | 
						|
    }
 | 
						|
 | 
						|
    for (AttributeEntry val : attributes) {
 | 
						|
      val.resize(MAX_FIELD_LEN);
 | 
						|
      attributes_.insert(val);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  MediaElementItem(const MediaElementItem&) = default;
 | 
						|
 | 
						|
  size_t size() const {
 | 
						|
    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 += name_.size();
 | 
						|
    ret += 1;  // Number of Attributes
 | 
						|
    for (const auto& entry : attributes_) {
 | 
						|
      ret += entry.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;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  size_t size() const {
 | 
						|
    switch (type_) {
 | 
						|
      case PLAYER:
 | 
						|
        return player_.size();
 | 
						|
      case FOLDER:
 | 
						|
        return folder_.size();
 | 
						|
      case SONG:
 | 
						|
        return song_.size();
 | 
						|
    }
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
constexpr size_t AVCT_HDR_LEN = 3;
 | 
						|
 | 
						|
}  // namespace avrcp
 | 
						|
}  // namespace bluetooth
 |