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
19namespace bluetooth {
20namespace avrcp {
21
22std::unique_ptr<GetFolderItemsResponseBuilder>
23GetFolderItemsResponseBuilder::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
33std::unique_ptr<GetFolderItemsResponseBuilder>
34GetFolderItemsResponseBuilder::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
43std::unique_ptr<GetFolderItemsResponseBuilder>
44GetFolderItemsResponseBuilder::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
54size_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
71bool 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
96bool 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
105bool 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
114bool 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
123void 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
139void 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
183void 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
200void 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
232Scope GetFolderItemsRequest::GetScope() const {
233  auto it = begin() + BrowsePacket::kMinSize();
234  return static_cast<Scope>(*it);
235}
236
237uint32_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
242uint32_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
247uint8_t GetFolderItemsRequest::GetNumAttributes() const {
248  auto it = begin() + BrowsePacket::kMinSize() + static_cast<size_t>(9);
249  return *it;
250}
251
252std::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
272bool 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
289std::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
304std::unique_ptr<GetFolderItemsRequestBuilder>
305GetFolderItemsRequestBuilder::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
315size_t GetFolderItemsRequestBuilder::size() const {
316  size_t len = GetFolderItemsRequest::kMinSize();
317  len += requested_attrs_.size() * sizeof(Attribute);
318  return len;
319}
320
321bool 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