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