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_PLAYER_APPLICATION_SETTING_ATTRIBUTES = 0x11, 54 LIST_PLAYER_APPLICATION_SETTING_VALUES = 0x12, 55 GET_CURRENT_PLAYER_APPLICATION_SETTING_VALUE = 0x13, 56 SET_PLAYER_APPLICATION_SETTING_VALUE = 0x14, 57 GET_ELEMENT_ATTRIBUTES = 0x20, 58 GET_PLAY_STATUS = 0x30, 59 REGISTER_NOTIFICATION = 0x31, 60 SET_ABSOLUTE_VOLUME = 0x50, 61 SET_ADDRESSED_PLAYER = 0x60, 62 PLAY_ITEM = 0x74, 63 }; 64 65 enum class PacketType : uint8_t { 66 SINGLE = 0x00, 67 }; 68 69 enum class Capability : uint8_t { 70 COMPANY_ID = 0x02, 71 EVENTS_SUPPORTED = 0x03, 72 }; 73 74 // Found in AVRCP_v1.6.1 Section 28 Appendix H 75 enum class Event : uint8_t { 76 PLAYBACK_STATUS_CHANGED = 0x01, 77 TRACK_CHANGED = 0x02, 78 PLAYBACK_POS_CHANGED = 0x05, 79 PLAYER_APPLICATION_SETTING_CHANGED = 0x08, 80 NOW_PLAYING_CONTENT_CHANGED = 0x09, 81 AVAILABLE_PLAYERS_CHANGED = 0x0a, 82 ADDRESSED_PLAYER_CHANGED = 0x0b, 83 UIDS_CHANGED = 0x0c, 84 VOLUME_CHANGED = 0x0d, 85 }; 86 87 enum class Attribute : uint32_t { 88 TITLE = 0x01, 89 ARTIST_NAME = 0x02, 90 ALBUM_NAME = 0x03, 91 TRACK_NUMBER = 0x04, 92 TOTAL_NUMBER_OF_TRACKS = 0x05, 93 GENRE = 0x06, 94 PLAYING_TIME = 0x07, 95 DEFAULT_COVER_ART = 0x08, 96 }; 97 98 enum class Status : uint8_t { 99 INVALID_COMMAND = 0x00, 100 INVALID_PARAMETER = 0x01, 101 PARAMETER_CONTENT_ERROR = 0x02, 102 INTERNAL_ERROR = 0x03, 103 NO_ERROR = 0x04, 104 UIDS_CHANGED = 0x05, 105 RESERVED = 0x06, 106 INVALID_DIRECTION = 0x07, 107 NOT_A_DIRECTORY = 0x08, 108 DOES_NOT_EXIST = 0x09, 109 INVALID_SCOPE = 0x0a, 110 RANGE_OUT_OF_BOUNDS = 0xb, 111 FOLDER_ITEM_NOT_PLAYABLE = 0x0c, 112 MEDIA_IN_USE = 0x0d, 113 NOW_PLAYING_LIST_FULL = 0x0e, 114 SEARCH_NOT_SUPPORTED = 0x0f, 115 SEARCH_IN_PROGRESS = 0x10, 116 INVALID_PLAYER_ID = 0x11, 117 PLAYER_NOT_BROWSABLE = 0x12, 118 PLAYER_NOT_ADDRESSED = 0x13, 119 NO_VALID_SEARCH_RESULTS = 0x14, 120 NO_AVAILABLE_PLAYERS = 0x15, 121 ADDRESSED_PLAYER_CHANGED = 0x16, 122 }; 123 124 enum class BrowsePdu : uint8_t { 125 SET_BROWSED_PLAYER = 0x70, 126 GET_FOLDER_ITEMS = 0x71, 127 CHANGE_PATH = 0x72, 128 GET_ITEM_ATTRIBUTES = 0x73, 129 GET_TOTAL_NUMBER_OF_ITEMS = 0x75, 130 GENERAL_REJECT = 0xa0, 131 }; 132 133 enum class Scope : uint8_t { 134 MEDIA_PLAYER_LIST = 0x00, 135 VFS = 0x01, 136 SEARCH = 0x02, 137 NOW_PLAYING = 0x03, 138 }; 139 140 enum class Direction : uint8_t { 141 UP = 0x00, 142 DOWN = 0x01, 143 }; 144 145 enum class KeyState : uint8_t { 146 PUSHED = 0x00, 147 RELEASED = 0x01, 148 }; 149 150 enum class PlayerAttribute : uint8_t { 151 EQUALIZER = 0x01, 152 REPEAT = 0x02, 153 SHUFFLE = 0x03, 154 SCAN = 0x04, 155 }; 156 157 enum class PlayerRepeatValue : uint8_t { 158 OFF = 0x01, 159 SINGLE = 0x02, 160 ALL = 0x03, 161 GROUP = 0x04, 162 }; 163 164 enum class PlayerShuffleValue : uint8_t { 165 OFF = 0x01, 166 ALL = 0x02, 167 GROUP = 0x03, 168 }; 169 170 class AttributeEntry { 171 public: AttributeEntry(const Attribute & attribute,const std::string & value)172 AttributeEntry(const Attribute& attribute, const std::string& value) 173 : attribute_(attribute), value_(value) {} 174 AttributeEntry(const Attribute & attribute)175 AttributeEntry(const Attribute& attribute) : attribute_(attribute) {} 176 177 AttributeEntry(const AttributeEntry&) = default; 178 attribute()179 Attribute attribute() const { return attribute_; } 180 value()181 std::string value() const { return value_; } 182 kHeaderSize()183 static constexpr size_t kHeaderSize() { 184 size_t ret = 0; 185 ret += 4; // Size of attribute field 186 ret += 2; // Size of length field 187 ret += 2; // Size of character encoding field 188 return ret; 189 } 190 size()191 size_t size() const { return kHeaderSize() + value_.size(); } 192 resize(size_t new_size)193 void resize(size_t new_size) { 194 new_size = new_size < kHeaderSize() ? 0 : new_size - kHeaderSize(); 195 if (value_.size() > new_size) { 196 value_.resize(new_size); 197 } 198 } 199 empty()200 bool empty() { return value_.empty(); } 201 202 bool operator<(const AttributeEntry& rhs) const { 203 return attribute_ < rhs.attribute_; 204 } 205 206 private: 207 Attribute attribute_; 208 std::string value_; 209 }; 210 211 constexpr size_t MAX_FIELD_LEN = 100; 212 213 struct MediaPlayerItem { 214 uint16_t id_; 215 std::string name_; 216 bool browsable_; 217 MediaPlayerItemMediaPlayerItem218 MediaPlayerItem(uint16_t id, const std::string& name, bool browsable) 219 : id_(id), name_(name), browsable_(browsable) { 220 if (name_.size() > MAX_FIELD_LEN) { 221 name_.resize(MAX_FIELD_LEN); 222 } 223 } 224 225 MediaPlayerItem(const MediaPlayerItem&) = default; 226 kHeaderSizeMediaPlayerItem227 static constexpr size_t kHeaderSize() { 228 size_t ret = 0; 229 ret += 1; // Media Player Type 230 ret += 2; // Item Length 231 ret += 2; // Player Id 232 ret += 1; // Player Type 233 ret += 4; // Player Subtype 234 ret += 1; // Play Status 235 ret += 16; // Features 236 ret += 2; // UTF-8 character set 237 ret += 2; // Name Length 238 return ret; 239 } 240 sizeMediaPlayerItem241 size_t size() const { return kHeaderSize() + name_.size(); } 242 }; 243 244 struct FolderItem { 245 uint64_t uid_; 246 uint8_t folder_type_; 247 bool is_playable_; 248 std::string name_; 249 FolderItemFolderItem250 FolderItem(uint64_t uid, uint8_t folder_type, bool is_playable, 251 const std::string& name) 252 : uid_(uid), 253 folder_type_(folder_type), 254 is_playable_(is_playable), 255 name_(name) { 256 if (name_.size() > MAX_FIELD_LEN) { 257 name_.resize(MAX_FIELD_LEN); 258 } 259 } 260 261 FolderItem(const FolderItem&) = default; 262 kHeaderSizeFolderItem263 static constexpr size_t kHeaderSize() { 264 size_t ret = 0; 265 ret += 1; // Folder Item Type 266 ret += 2; // Item Length 267 ret += 8; // Folder UID 268 ret += 1; // Folder Type 269 ret += 1; // Is Playable byte 270 ret += 2; // UTF-8 Character Set 271 ret += 2; // Name Length 272 return ret; 273 } 274 sizeFolderItem275 size_t size() const { return kHeaderSize() + name_.size(); } 276 }; 277 278 // NOTE: We never use media type field because we only support audio types 279 struct MediaElementItem { 280 uint64_t uid_ = 0; 281 std::string name_; 282 std::set<AttributeEntry> attributes_; 283 284 // Truncate the name and attribute fields so that we don't have a single item 285 // that can exceed the Browsing MTU MediaElementItemMediaElementItem286 MediaElementItem(uint64_t uid, const std::string& name, 287 std::set<AttributeEntry> attributes) 288 : uid_(uid), name_(name) { 289 if (name_.size() > MAX_FIELD_LEN) { 290 name_.resize(MAX_FIELD_LEN); 291 } 292 293 for (AttributeEntry val : attributes) { 294 val.resize(MAX_FIELD_LEN); 295 attributes_.insert(val); 296 } 297 } 298 299 MediaElementItem(const MediaElementItem&) = default; 300 sizeMediaElementItem301 size_t size() const { 302 size_t ret = 0; 303 ret += 1; // Media Element Item Type 304 ret += 2; // Item Length 305 ret += 8; // Item UID 306 ret += 1; // Media Type 307 ret += 2; // UTF-8 Character Set 308 ret += 2; // Name Length 309 ret += name_.size(); 310 ret += 1; // Number of Attributes 311 for (const auto& entry : attributes_) { 312 ret += entry.size(); 313 } 314 315 return ret; 316 } 317 }; 318 319 struct MediaListItem { 320 enum : uint8_t { PLAYER = 0x01, FOLDER = 0x02, SONG = 0x03 } type_; 321 322 union { 323 MediaPlayerItem player_; 324 FolderItem folder_; 325 MediaElementItem song_; 326 }; 327 MediaListItemMediaListItem328 MediaListItem(MediaPlayerItem item) : type_(PLAYER), player_(item) {} 329 MediaListItemMediaListItem330 MediaListItem(FolderItem item) : type_(FOLDER), folder_(item) {} 331 MediaListItemMediaListItem332 MediaListItem(MediaElementItem item) : type_(SONG), song_(item) {} 333 MediaListItemMediaListItem334 MediaListItem(const MediaListItem& item) { 335 type_ = item.type_; 336 switch (item.type_) { 337 case PLAYER: 338 new (&player_) MediaPlayerItem(item.player_); 339 return; 340 case FOLDER: 341 new (&folder_) FolderItem(item.folder_); 342 return; 343 case SONG: 344 new (&song_) MediaElementItem(item.song_); 345 return; 346 } 347 } 348 ~MediaListItemMediaListItem349 ~MediaListItem() { 350 switch (type_) { 351 case PLAYER: 352 player_.~MediaPlayerItem(); 353 return; 354 case FOLDER: 355 folder_.~FolderItem(); 356 return; 357 case SONG: 358 song_.~MediaElementItem(); 359 return; 360 } 361 } 362 sizeMediaListItem363 size_t size() const { 364 switch (type_) { 365 case PLAYER: 366 return player_.size(); 367 case FOLDER: 368 return folder_.size(); 369 case SONG: 370 return song_.size(); 371 } 372 } 373 }; 374 375 constexpr size_t AVCT_HDR_LEN = 3; 376 377 } // namespace avrcp 378 } // namespace bluetooth