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