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 #include "get_folder_items.h"
18 
19 namespace bluetooth {
20 namespace avrcp {
21 
22 std::unique_ptr<GetFolderItemsResponseBuilder>
MakePlayerListBuilder(Status status,uint16_t uid_counter,size_t mtu)23 GetFolderItemsResponseBuilder::MakePlayerListBuilder(Status status,
24                                                      uint16_t uid_counter,
25                                                      size_t mtu) {
26   std::unique_ptr<GetFolderItemsResponseBuilder> builder(
27       new GetFolderItemsResponseBuilder(Scope::MEDIA_PLAYER_LIST, status,
28                                         uid_counter, mtu));
29 
30   return builder;
31 }
32 
33 std::unique_ptr<GetFolderItemsResponseBuilder>
MakeVFSBuilder(Status status,uint16_t uid_counter,size_t mtu)34 GetFolderItemsResponseBuilder::MakeVFSBuilder(Status status,
35                                               uint16_t uid_counter,
36                                               size_t mtu) {
37   std::unique_ptr<GetFolderItemsResponseBuilder> builder(
38       new GetFolderItemsResponseBuilder(Scope::VFS, status, uid_counter, mtu));
39 
40   return builder;
41 }
42 
43 std::unique_ptr<GetFolderItemsResponseBuilder>
MakeNowPlayingBuilder(Status status,uint16_t uid_counter,size_t mtu)44 GetFolderItemsResponseBuilder::MakeNowPlayingBuilder(Status status,
45                                                      uint16_t uid_counter,
46                                                      size_t mtu) {
47   std::unique_ptr<GetFolderItemsResponseBuilder> builder(
48       new GetFolderItemsResponseBuilder(Scope::NOW_PLAYING, status, uid_counter,
49                                         mtu));
50 
51   return builder;
52 }
53 
size() const54 size_t GetFolderItemsResponseBuilder::size() const {
55   size_t len = BrowsePacket::kMinSize();
56   len += 1;  // Status
57 
58   // There is nothing other than the status in the packet if the status isn't
59   // NO_ERROR
60   if (status_ != Status::NO_ERROR || items_.size() == 0) return len;
61 
62   len += 2;  // UID Counter
63   len += 2;  // Number of Items;
64   for (const auto& item : items_) {
65     len += item.size();
66   }
67 
68   return len;
69 }
70 
Serialize(const std::shared_ptr<::bluetooth::Packet> & pkt)71 bool GetFolderItemsResponseBuilder::Serialize(
72     const std::shared_ptr<::bluetooth::Packet>& pkt) {
73   ReserveSpace(pkt, size());
74 
75   BrowsePacketBuilder::PushHeader(pkt, size() - BrowsePacket::kMinSize());
76 
77   if (status_ == Status::NO_ERROR && items_.size() == 0) {
78     // Return range out of bounds if there are zero items in the folder
79     status_ = Status::RANGE_OUT_OF_BOUNDS;
80   }
81 
82   AddPayloadOctets1(pkt, (uint8_t)status_);  // Status
83   if (status_ != Status::NO_ERROR) return true;
84 
85   AddPayloadOctets2(pkt, base::ByteSwap(uid_counter_));
86   uint16_t num_items = items_.size();
87   AddPayloadOctets2(pkt, base::ByteSwap(num_items));
88 
89   for (const auto& item : items_) {
90     PushMediaListItem(pkt, item);
91   }
92 
93   return true;
94 }
95 
AddMediaPlayer(MediaPlayerItem item)96 bool GetFolderItemsResponseBuilder::AddMediaPlayer(MediaPlayerItem item) {
97   CHECK(scope_ == Scope::MEDIA_PLAYER_LIST);
98 
99   if (size() + item.size() > mtu_) return false;
100 
101   items_.push_back(MediaListItem(item));
102   return true;
103 }
104 
AddSong(MediaElementItem item)105 bool GetFolderItemsResponseBuilder::AddSong(MediaElementItem item) {
106   CHECK(scope_ == Scope::VFS || scope_ == Scope::NOW_PLAYING);
107 
108   if (size() + item.size() > mtu_) return false;
109 
110   items_.push_back(MediaListItem(item));
111   return true;
112 }
113 
AddFolder(FolderItem item)114 bool GetFolderItemsResponseBuilder::AddFolder(FolderItem item) {
115   CHECK(scope_ == Scope::VFS);
116 
117   if (size() + item.size() > mtu_) return false;
118 
119   items_.push_back(MediaListItem(item));
120   return true;
121 }
122 
PushMediaListItem(const std::shared_ptr<::bluetooth::Packet> & pkt,const MediaListItem & item)123 void GetFolderItemsResponseBuilder::PushMediaListItem(
124     const std::shared_ptr<::bluetooth::Packet>& pkt,
125     const MediaListItem& item) {
126   switch (item.type_) {
127     case MediaListItem::PLAYER:
128       PushMediaPlayerItem(pkt, item.player_);
129       break;
130     case MediaListItem::FOLDER:
131       PushFolderItem(pkt, item.folder_);
132       break;
133     case MediaListItem::SONG:
134       PushMediaElementItem(pkt, item.song_);
135       break;
136   }
137 }
138 
PushMediaPlayerItem(const std::shared_ptr<::bluetooth::Packet> & pkt,const MediaPlayerItem & item)139 void GetFolderItemsResponseBuilder::PushMediaPlayerItem(
140     const std::shared_ptr<::bluetooth::Packet>& pkt,
141     const MediaPlayerItem& item) {
142   AddPayloadOctets1(pkt, 0x01);  // Media Player Item
143   uint16_t item_len = item.size() - 3;
144   AddPayloadOctets2(pkt, base::ByteSwap(item_len));  // Item length
145   AddPayloadOctets2(pkt, base::ByteSwap(item.id_));  // Player ID
146   AddPayloadOctets1(pkt, 0x01);                      // Player Type
147   AddPayloadOctets4(pkt, 0x00000000);                // Player Subtype
148   AddPayloadOctets1(
149       pkt, 0x02);  // Player Play Status // TODO: Add this as a passed field
150 
151   // Features
152   AddPayloadOctets1(pkt, 0x00);
153   AddPayloadOctets1(pkt, 0x00);
154   AddPayloadOctets1(pkt, 0x00);
155   AddPayloadOctets1(pkt, 0x00);
156   AddPayloadOctets1(pkt, 0x00);
157   AddPayloadOctets1(pkt, 0xb7);
158   AddPayloadOctets1(pkt, 0x01);
159   if (item.browsable_) {
160     AddPayloadOctets1(pkt, 0x0C);
161     AddPayloadOctets1(pkt, 0x0a);
162   } else {
163     AddPayloadOctets1(pkt, 0x04);
164     AddPayloadOctets1(pkt, 0x00);
165   }
166   AddPayloadOctets1(pkt, 0x00);
167   AddPayloadOctets1(pkt, 0x00);
168   AddPayloadOctets1(pkt, 0x00);
169   AddPayloadOctets1(pkt, 0x00);
170   AddPayloadOctets1(pkt, 0x00);
171   AddPayloadOctets1(pkt, 0x00);
172   AddPayloadOctets1(pkt, 0x00);
173 
174   AddPayloadOctets2(pkt, base::ByteSwap((uint16_t)0x006a));
175   uint16_t name_len = item.name_.size();
176   AddPayloadOctets2(pkt, base::ByteSwap(name_len));
177 
178   for (const uint8_t& byte : item.name_) {
179     AddPayloadOctets1(pkt, byte);
180   }
181 }
182 
PushFolderItem(const std::shared_ptr<::bluetooth::Packet> & pkt,const FolderItem & item)183 void GetFolderItemsResponseBuilder::PushFolderItem(
184     const std::shared_ptr<::bluetooth::Packet>& pkt, const FolderItem& item) {
185   AddPayloadOctets1(pkt, 0x02);  // Folder Item
186   uint16_t item_len = item.size() - 3;
187   AddPayloadOctets2(pkt, base::ByteSwap(item_len));
188   AddPayloadOctets8(pkt, base::ByteSwap(item.uid_));
189   AddPayloadOctets1(pkt, item.folder_type_);
190   AddPayloadOctets1(pkt, item.is_playable_ ? 0x01 : 0x00);
191   AddPayloadOctets2(pkt,
192                     base::ByteSwap((uint16_t)0x006a));  // UTF-8 Character Set
193   uint16_t name_len = item.name_.size();
194   AddPayloadOctets2(pkt, base::ByteSwap(name_len));
195   for (const uint8_t& byte : item.name_) {
196     AddPayloadOctets1(pkt, byte);
197   }
198 }
199 
PushMediaElementItem(const std::shared_ptr<::bluetooth::Packet> & pkt,const MediaElementItem & item)200 void GetFolderItemsResponseBuilder::PushMediaElementItem(
201     const std::shared_ptr<::bluetooth::Packet>& pkt,
202     const MediaElementItem& item) {
203   AddPayloadOctets1(pkt, 0x03);  // Media Element Item
204   uint16_t item_len = item.size() - 3;
205   AddPayloadOctets2(pkt, base::ByteSwap(item_len));
206   AddPayloadOctets8(pkt, base::ByteSwap(item.uid_));
207   AddPayloadOctets1(pkt, 0x00);  // Media Type Audio
208   AddPayloadOctets2(pkt,
209                     base::ByteSwap((uint16_t)0x006a));  // UTF-8 Character Set
210   uint16_t name_len = item.name_.size();
211   AddPayloadOctets2(pkt, base::ByteSwap(name_len));
212   for (const uint8_t& byte : item.name_) {
213     AddPayloadOctets1(pkt, byte);
214   }
215 
216   AddPayloadOctets1(pkt, (uint8_t)item.attributes_.size());
217   for (const auto& entry : item.attributes_) {
218     AddPayloadOctets4(pkt, base::ByteSwap((uint32_t)entry.attribute()));
219     AddPayloadOctets2(pkt,
220                       base::ByteSwap((uint16_t)0x006a));  // UTF-8 Character Set
221 
222     std::string attr_val = entry.value();
223     uint16_t attr_len = attr_val.size();
224 
225     AddPayloadOctets2(pkt, base::ByteSwap(attr_len));
226     for (const uint8_t& byte : attr_val) {
227       AddPayloadOctets1(pkt, byte);
228     }
229   }
230 }
231 
GetScope() const232 Scope GetFolderItemsRequest::GetScope() const {
233   auto it = begin() + BrowsePacket::kMinSize();
234   return static_cast<Scope>(*it);
235 }
236 
GetStartItem() const237 uint32_t GetFolderItemsRequest::GetStartItem() const {
238   auto it = begin() + BrowsePacket::kMinSize() + static_cast<size_t>(1);
239   return base::ByteSwap(it.extract<uint32_t>());
240 }
241 
GetEndItem() const242 uint32_t GetFolderItemsRequest::GetEndItem() const {
243   auto it = begin() + BrowsePacket::kMinSize() + static_cast<size_t>(5);
244   return base::ByteSwap(it.extract<uint32_t>());
245 }
246 
GetNumAttributes() const247 uint8_t GetFolderItemsRequest::GetNumAttributes() const {
248   auto it = begin() + BrowsePacket::kMinSize() + static_cast<size_t>(9);
249   return *it;
250 }
251 
GetAttributesRequested() const252 std::vector<Attribute> GetFolderItemsRequest::GetAttributesRequested() const {
253   auto it = begin() + BrowsePacket::kMinSize() + static_cast<size_t>(9);
254 
255   size_t number_of_attributes = it.extract<uint8_t>();
256   std::vector<Attribute> attribute_list;
257 
258   // No attributes requested
259   if (number_of_attributes == 0xFF) return attribute_list;
260 
261   // TODO: If the number of attributes equals 0, then all attributes are
262   // requested right now thats handled in the service itself, but it'd be nice
263   // to have this function return a vector with all the attributes
264 
265   for (size_t i = 0; i < number_of_attributes; i++) {
266     attribute_list.push_back((Attribute)base::ByteSwap(it.extract<uint32_t>()));
267   }
268 
269   return attribute_list;
270 }
271 
IsValid() const272 bool GetFolderItemsRequest::IsValid() const {
273   if (!BrowsePacket::IsValid()) return false;
274   // The minimum size required to be valid
275   if (size() < kMinSize()) return false;
276 
277   auto attr_count = GetNumAttributes();
278 
279   // No items requested
280   if (attr_count == 0xFF) return true;
281 
282   auto attr_start = begin() + kMinSize();
283 
284   // Casting the int returned from end - attr_start should be fine. If an
285   // overflow occurs we can definitly say the packet is invalid
286   return (attr_count * sizeof(Attribute)) == (size_t)(end() - attr_start);
287 }
288 
ToString() const289 std::string GetFolderItemsRequest::ToString() const {
290   std::stringstream ss;
291   ss << "GetFolderItemsRequestPacket: " << std::endl;
292   ss << "  └ PDU = " << GetPdu() << std::endl;
293   ss << "  └ Length = " << GetLength() << std::endl;
294   ss << "  └ Scope = " << GetScope() << std::endl;
295   ss << "  └ Start Item = " << loghex(GetStartItem()) << std::endl;
296   ss << "  └ End Item = " << loghex(GetEndItem()) << std::endl;
297   ss << "  └ Attribute Count = " << loghex(GetNumAttributes()) << std::endl;
298 
299   ss << std::endl;
300 
301   return ss.str();
302 }
303 
304 std::unique_ptr<GetFolderItemsRequestBuilder>
MakeBuilder(Scope scope,uint32_t start_item,uint32_t end_item,const std::set<Attribute> & requested_attrs)305 GetFolderItemsRequestBuilder::MakeBuilder(
306     Scope scope, uint32_t start_item, uint32_t end_item,
307     const std::set<Attribute>& requested_attrs) {
308   std::unique_ptr<GetFolderItemsRequestBuilder> builder(
309       new GetFolderItemsRequestBuilder(scope, start_item, end_item,
310                                        requested_attrs));
311 
312   return builder;
313 }
314 
size() const315 size_t GetFolderItemsRequestBuilder::size() const {
316   size_t len = GetFolderItemsRequest::kMinSize();
317   len += requested_attrs_.size() * sizeof(Attribute);
318   return len;
319 }
320 
Serialize(const std::shared_ptr<::bluetooth::Packet> & pkt)321 bool GetFolderItemsRequestBuilder::Serialize(
322     const std::shared_ptr<::bluetooth::Packet>& pkt) {
323   ReserveSpace(pkt, size());
324 
325   BrowsePacketBuilder::PushHeader(pkt, size() - BrowsePacket::kMinSize());
326 
327   AddPayloadOctets1(pkt, static_cast<uint8_t>(scope_));
328   AddPayloadOctets4(pkt, base::ByteSwap(start_item_));
329   AddPayloadOctets4(pkt, base::ByteSwap(end_item_));
330 
331   if (requested_attrs_.size() == 0) {
332     // 0xFF is the value to signify that there are no attributes requested.
333     AddPayloadOctets1(pkt, 0xFF);
334     return true;
335   }
336 
337   AddPayloadOctets1(pkt, requested_attrs_.size());
338   for (const auto& attr : requested_attrs_) {
339     AddPayloadOctets4(pkt, base::ByteSwap(static_cast<uint32_t>(attr)));
340   }
341   return true;
342 }
343 
344 }  // namespace avrcp
345 }  // namespace bluetooth