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 namespace bluetooth {
20 namespace avrcp {
21
22 std::unique_ptr<GetFolderItemsResponseBuilder>
MakePlayerListBuilder(Status status,uint16_t uid_counter,size_t mtu)23 GetFolderItemsResponseBuilder::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
33 std::unique_ptr<GetFolderItemsResponseBuilder>
MakeVFSBuilder(Status status,uint16_t uid_counter,size_t mtu)34 GetFolderItemsResponseBuilder::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
43 std::unique_ptr<GetFolderItemsResponseBuilder>
MakeNowPlayingBuilder(Status status,uint16_t uid_counter,size_t mtu)44 GetFolderItemsResponseBuilder::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
size() const54 size_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
Serialize(const std::shared_ptr<::bluetooth::Packet> & pkt)71 bool 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
AddMediaPlayer(MediaPlayerItem item)96 bool 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
AddSong(MediaElementItem item)105 bool 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
AddFolder(FolderItem item)114 bool 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
PushMediaListItem(const std::shared_ptr<::bluetooth::Packet> & pkt,const MediaListItem & item)123 void 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
PushMediaPlayerItem(const std::shared_ptr<::bluetooth::Packet> & pkt,const MediaPlayerItem & item)139 void 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
PushFolderItem(const std::shared_ptr<::bluetooth::Packet> & pkt,const FolderItem & item)183 void 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
PushMediaElementItem(const std::shared_ptr<::bluetooth::Packet> & pkt,const MediaElementItem & item)200 void 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
GetScope() const232 Scope GetFolderItemsRequest::GetScope() const {
233 auto it = begin() + BrowsePacket::kMinSize();
234 return static_cast<Scope>(*it);
235 }
236
GetStartItem() const237 uint32_t GetFolderItemsRequest::GetStartItem() const {
238 auto it = begin() + BrowsePacket::kMinSize() + static_cast<size_t>(1);
239 return it.extractBE<uint32_t>();
240 }
241
GetEndItem() const242 uint32_t GetFolderItemsRequest::GetEndItem() const {
243 auto it = begin() + BrowsePacket::kMinSize() + static_cast<size_t>(5);
244 return it.extractBE<uint32_t>();
245 }
246
GetNumAttributes() const247 uint8_t GetFolderItemsRequest::GetNumAttributes() const {
248 auto it = begin() + BrowsePacket::kMinSize() + static_cast<size_t>(9);
249 return *it;
250 }
251
GetAttributesRequested() const252 std::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)it.extractBE<uint32_t>());
267 }
268
269 return attribute_list;
270 }
271
IsValid() const272 bool 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
ToString() const289 std::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
304 std::unique_ptr<GetFolderItemsRequestBuilder>
MakeBuilder(Scope scope,uint32_t start_item,uint32_t end_item,const std::set<Attribute> & requested_attrs)305 GetFolderItemsRequestBuilder::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
size() const315 size_t GetFolderItemsRequestBuilder::size() const {
316 size_t len = GetFolderItemsRequest::kMinSize();
317 len += requested_attrs_.size() * sizeof(Attribute);
318 return len;
319 }
320
Serialize(const std::shared_ptr<::bluetooth::Packet> & pkt)321 bool 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
346