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