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