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