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