1 /* 2 * Copyright 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #pragma once 18 19 #include <set> 20 21 #include <base/sys_byteorder.h> 22 23 namespace bluetooth { 24 namespace avrcp { 25 26 constexpr uint32_t BLUETOOTH_COMPANY_ID = 0x001958; 27 28 constexpr uint8_t MAX_TRANSACTION_LABEL = 0xF; 29 30 enum class CType : uint8_t { 31 CONTROL = 0x0, 32 STATUS = 0x1, 33 NOTIFY = 0x3, 34 NOT_IMPLEMENTED = 0x8, 35 ACCEPTED = 0x9, 36 REJECTED = 0xa, 37 STABLE = 0xc, 38 CHANGED = 0xd, 39 INTERIM = 0xf, 40 }; 41 42 enum class Opcode : uint8_t { 43 VENDOR = 0x00, 44 UNIT_INFO = 0x30, 45 SUBUNIT_INFO = 0x31, 46 PASS_THROUGH = 0x7c, 47 }; 48 49 // Found in AVRCP_v1.6.1 Section 4.5 Table 4.5 50 // Searching can be done in the spec by Camel Casing the constant name 51 enum class CommandPdu : uint8_t { 52 GET_CAPABILITIES = 0x10, 53 LIST_APPLICATION_SETTING_ATTRIBUTES = 0x11, 54 GET_ELEMENT_ATTRIBUTES = 0x20, 55 GET_PLAY_STATUS = 0x30, 56 REGISTER_NOTIFICATION = 0x31, 57 SET_ABSOLUTE_VOLUME = 0x50, 58 SET_ADDRESSED_PLAYER = 0x60, 59 PLAY_ITEM = 0x74, 60 }; 61 62 enum class PacketType : uint8_t { 63 SINGLE = 0x00, 64 }; 65 66 enum class Capability : uint8_t { 67 COMPANY_ID = 0x02, 68 EVENTS_SUPPORTED = 0x03, 69 }; 70 71 // Found in AVRCP_v1.6.1 Section 28 Appendix H 72 enum class Event : uint8_t { 73 PLAYBACK_STATUS_CHANGED = 0x01, 74 TRACK_CHANGED = 0x02, 75 PLAYBACK_POS_CHANGED = 0x05, 76 PLAYER_APPLICATION_SETTING_CHANGED = 0x08, 77 NOW_PLAYING_CONTENT_CHANGED = 0x09, 78 AVAILABLE_PLAYERS_CHANGED = 0x0a, 79 ADDRESSED_PLAYER_CHANGED = 0x0b, 80 UIDS_CHANGED = 0x0c, 81 VOLUME_CHANGED = 0x0d, 82 }; 83 84 enum class Attribute : uint32_t { 85 TITLE = 0x01, 86 ARTIST_NAME = 0x02, 87 ALBUM_NAME = 0x03, 88 TRACK_NUMBER = 0x04, 89 TOTAL_NUMBER_OF_TRACKS = 0x05, 90 GENRE = 0x06, 91 PLAYING_TIME = 0x07, 92 DEFAULT_COVER_ART = 0x08, 93 }; 94 95 enum class Status : uint8_t { 96 INVALID_COMMAND = 0x00, 97 INVALID_PARAMETER = 0x01, 98 PARAMETER_CONTENT_ERROR = 0x02, 99 INTERNAL_ERROR = 0x03, 100 NO_ERROR = 0x04, 101 UIDS_CHANGED = 0x05, 102 RESERVED = 0x06, 103 INVALID_DIRECTION = 0x07, 104 NOT_A_DIRECTORY = 0x08, 105 DOES_NOT_EXIST = 0x09, 106 INVALID_SCOPE = 0x0a, 107 RANGE_OUT_OF_BOUNDS = 0xb, 108 FOLDER_ITEM_NOT_PLAYABLE = 0x0c, 109 MEDIA_IN_USE = 0x0d, 110 NOW_PLAYING_LIST_FULL = 0x0e, 111 SEARCH_NOT_SUPPORTED = 0x0f, 112 SEARCH_IN_PROGRESS = 0x10, 113 INVALID_PLAYER_ID = 0x11, 114 PLAYER_NOT_BROWSABLE = 0x12, 115 PLAYER_NOT_ADDRESSED = 0x13, 116 NO_VALID_SEARCH_RESULTS = 0x14, 117 NO_AVAILABLE_PLAYERS = 0x15, 118 ADDRESSED_PLAYER_CHANGED = 0x16, 119 }; 120 121 enum class BrowsePdu : uint8_t { 122 SET_BROWSED_PLAYER = 0x70, 123 GET_FOLDER_ITEMS = 0x71, 124 CHANGE_PATH = 0x72, 125 GET_ITEM_ATTRIBUTES = 0x73, 126 GET_TOTAL_NUMBER_OF_ITEMS = 0x75, 127 GENERAL_REJECT = 0xa0, 128 }; 129 130 enum class Scope : uint8_t { 131 MEDIA_PLAYER_LIST = 0x00, 132 VFS = 0x01, 133 SEARCH = 0x02, 134 NOW_PLAYING = 0x03, 135 }; 136 137 enum class Direction : uint8_t { 138 UP = 0x00, 139 DOWN = 0x01, 140 }; 141 142 enum class KeyState : uint8_t { 143 PUSHED = 0x00, 144 RELEASED = 0x01, 145 }; 146 147 class AttributeEntry { 148 public: AttributeEntry(const Attribute & attribute,const std::string & value)149 AttributeEntry(const Attribute& attribute, const std::string& value) 150 : attribute_(attribute), value_(value) {} 151 AttributeEntry(const Attribute & attribute)152 AttributeEntry(const Attribute& attribute) : attribute_(attribute) {} 153 154 AttributeEntry(const AttributeEntry&) = default; 155 attribute()156 Attribute attribute() const { return attribute_; } 157 value()158 std::string value() const { return value_; } 159 kHeaderSize()160 static constexpr size_t kHeaderSize() { 161 size_t ret = 0; 162 ret += 4; // Size of attribute field 163 ret += 2; // Size of length field 164 ret += 2; // Size of character encoding field 165 return ret; 166 } 167 size()168 size_t size() const { return kHeaderSize() + value_.size(); } 169 resize(size_t new_size)170 void resize(size_t new_size) { 171 new_size = new_size < kHeaderSize() ? 0 : new_size - kHeaderSize(); 172 if (value_.size() > new_size) { 173 value_.resize(new_size); 174 } 175 } 176 empty()177 bool empty() { return value_.empty(); } 178 179 bool operator<(const AttributeEntry& rhs) const { 180 return attribute_ < rhs.attribute_; 181 } 182 183 private: 184 Attribute attribute_; 185 std::string value_; 186 }; 187 188 constexpr size_t MAX_FIELD_LEN = 100; 189 190 struct MediaPlayerItem { 191 uint16_t id_; 192 std::string name_; 193 bool browsable_; 194 MediaPlayerItemMediaPlayerItem195 MediaPlayerItem(uint16_t id, const std::string& name, bool browsable) 196 : id_(id), name_(name), browsable_(browsable) { 197 if (name_.size() > MAX_FIELD_LEN) { 198 name_.resize(MAX_FIELD_LEN); 199 } 200 } 201 202 MediaPlayerItem(const MediaPlayerItem&) = default; 203 kHeaderSizeMediaPlayerItem204 static constexpr size_t kHeaderSize() { 205 size_t ret = 0; 206 ret += 1; // Media Player Type 207 ret += 2; // Item Length 208 ret += 2; // Player Id 209 ret += 1; // Player Type 210 ret += 4; // Player Subtype 211 ret += 1; // Play Status 212 ret += 16; // Features 213 ret += 2; // UTF-8 character set 214 ret += 2; // Name Length 215 return ret; 216 } 217 sizeMediaPlayerItem218 size_t size() const { return kHeaderSize() + name_.size(); } 219 }; 220 221 struct FolderItem { 222 uint64_t uid_; 223 uint8_t folder_type_; 224 bool is_playable_; 225 std::string name_; 226 FolderItemFolderItem227 FolderItem(uint64_t uid, uint8_t folder_type, bool is_playable, 228 const std::string& name) 229 : uid_(uid), 230 folder_type_(folder_type), 231 is_playable_(is_playable), 232 name_(name) { 233 if (name_.size() > MAX_FIELD_LEN) { 234 name_.resize(MAX_FIELD_LEN); 235 } 236 } 237 238 FolderItem(const FolderItem&) = default; 239 kHeaderSizeFolderItem240 static constexpr size_t kHeaderSize() { 241 size_t ret = 0; 242 ret += 1; // Folder Item Type 243 ret += 2; // Item Length 244 ret += 8; // Folder UID 245 ret += 1; // Folder Type 246 ret += 1; // Is Playable byte 247 ret += 2; // UTF-8 Character Set 248 ret += 2; // Name Length 249 return ret; 250 } 251 sizeFolderItem252 size_t size() const { return kHeaderSize() + name_.size(); } 253 }; 254 255 // NOTE: We never use media type field because we only support audio types 256 struct MediaElementItem { 257 uint64_t uid_ = 0; 258 std::string name_; 259 std::set<AttributeEntry> attributes_; 260 261 // Truncate the name and attribute fields so that we don't have a single item 262 // that can exceed the Browsing MTU MediaElementItemMediaElementItem263 MediaElementItem(uint64_t uid, const std::string& name, 264 std::set<AttributeEntry> attributes) 265 : uid_(uid), name_(name) { 266 if (name_.size() > MAX_FIELD_LEN) { 267 name_.resize(MAX_FIELD_LEN); 268 } 269 270 for (AttributeEntry val : attributes) { 271 val.resize(MAX_FIELD_LEN); 272 attributes_.insert(val); 273 } 274 } 275 276 MediaElementItem(const MediaElementItem&) = default; 277 sizeMediaElementItem278 size_t size() const { 279 size_t ret = 0; 280 ret += 1; // Media Element Item Type 281 ret += 2; // Item Length 282 ret += 8; // Item UID 283 ret += 1; // Media Type 284 ret += 2; // UTF-8 Character Set 285 ret += 2; // Name Length 286 ret += name_.size(); 287 ret += 1; // Number of Attributes 288 for (const auto& entry : attributes_) { 289 ret += entry.size(); 290 } 291 292 return ret; 293 } 294 }; 295 296 struct MediaListItem { 297 enum : uint8_t { PLAYER = 0x01, FOLDER = 0x02, SONG = 0x03 } type_; 298 299 union { 300 MediaPlayerItem player_; 301 FolderItem folder_; 302 MediaElementItem song_; 303 }; 304 MediaListItemMediaListItem305 MediaListItem(MediaPlayerItem item) : type_(PLAYER), player_(item) {} 306 MediaListItemMediaListItem307 MediaListItem(FolderItem item) : type_(FOLDER), folder_(item) {} 308 MediaListItemMediaListItem309 MediaListItem(MediaElementItem item) : type_(SONG), song_(item) {} 310 MediaListItemMediaListItem311 MediaListItem(const MediaListItem& item) { 312 type_ = item.type_; 313 switch (item.type_) { 314 case PLAYER: 315 new (&player_) MediaPlayerItem(item.player_); 316 return; 317 case FOLDER: 318 new (&folder_) FolderItem(item.folder_); 319 return; 320 case SONG: 321 new (&song_) MediaElementItem(item.song_); 322 return; 323 } 324 } 325 ~MediaListItemMediaListItem326 ~MediaListItem() { 327 switch (type_) { 328 case PLAYER: 329 player_.~MediaPlayerItem(); 330 return; 331 case FOLDER: 332 folder_.~FolderItem(); 333 return; 334 case SONG: 335 song_.~MediaElementItem(); 336 return; 337 } 338 } 339 sizeMediaListItem340 size_t size() const { 341 switch (type_) { 342 case PLAYER: 343 return player_.size(); 344 case FOLDER: 345 return folder_.size(); 346 case SONG: 347 return song_.size(); 348 } 349 } 350 }; 351 352 constexpr size_t AVCT_HDR_LEN = 3; 353 354 } // namespace avrcp 355 } // namespace bluetooth