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 <base/message_loop/message_loop.h> 18 19 #include "connection_handler.h" 20 #include "device.h" 21 #include "stack_config.h" 22 23 namespace bluetooth { 24 namespace avrcp { 25 26 #define DEVICE_LOG(LEVEL) LOG(LEVEL) << address_.ToString() << " : " 27 #define DEVICE_VLOG(LEVEL) VLOG(LEVEL) << address_.ToString() << " : " 28 29 #define VOL_NOT_SUPPORTED -1 30 #define VOL_REGISTRATION_FAILED -2 31 32 Device::Device( 33 const RawAddress& bdaddr, bool avrcp13_compatibility, 34 base::Callback<void(uint8_t label, bool browse, 35 std::unique_ptr<::bluetooth::PacketBuilder> message)> 36 send_msg_cb, 37 uint16_t ctrl_mtu, uint16_t browse_mtu) 38 : weak_ptr_factory_(this), 39 address_(bdaddr), 40 avrcp13_compatibility_(avrcp13_compatibility), 41 send_message_cb_(send_msg_cb), 42 ctrl_mtu_(ctrl_mtu), 43 browse_mtu_(browse_mtu) {} 44 45 void Device::RegisterInterfaces(MediaInterface* media_interface, 46 A2dpInterface* a2dp_interface, 47 VolumeInterface* volume_interface) { 48 CHECK(media_interface); 49 CHECK(a2dp_interface); 50 a2dp_interface_ = a2dp_interface; 51 media_interface_ = media_interface; 52 volume_interface_ = volume_interface; 53 } 54 55 bool Device::IsActive() const { 56 return address_ == a2dp_interface_->active_peer(); 57 } 58 59 void Device::VendorPacketHandler(uint8_t label, 60 std::shared_ptr<VendorPacket> pkt) { 61 CHECK(media_interface_); 62 DEVICE_VLOG(3) << __func__ << ": pdu=" << pkt->GetCommandPdu(); 63 64 // All CTypes at and above NOT_IMPLEMENTED are all response types. 65 if (pkt->GetCType() == CType::NOT_IMPLEMENTED) { 66 return; 67 } 68 69 if (pkt->GetCType() >= CType::ACCEPTED) { 70 switch (pkt->GetCommandPdu()) { 71 // VOLUME_CHANGED is the only notification we register for while target. 72 case CommandPdu::REGISTER_NOTIFICATION: { 73 auto register_notification = 74 Packet::Specialize<RegisterNotificationResponse>(pkt); 75 if (register_notification->GetEvent() != Event::VOLUME_CHANGED) { 76 DEVICE_LOG(WARNING) 77 << __func__ << ": Unhandled register notification received: " 78 << register_notification->GetEvent(); 79 return; 80 } 81 HandleVolumeChanged(label, register_notification); 82 break; 83 } 84 case CommandPdu::SET_ABSOLUTE_VOLUME: 85 // TODO (apanicke): Add a retry mechanism if the response has a 86 // different volume than the one we set. For now, we don't care 87 // about the response to this message. 88 break; 89 default: 90 DEVICE_LOG(WARNING) 91 << __func__ << ": Unhandled Response: pdu=" << pkt->GetCommandPdu(); 92 break; 93 } 94 return; 95 } 96 97 switch (pkt->GetCommandPdu()) { 98 case CommandPdu::GET_CAPABILITIES: { 99 HandleGetCapabilities(label, 100 Packet::Specialize<GetCapabilitiesRequest>(pkt)); 101 } break; 102 103 case CommandPdu::REGISTER_NOTIFICATION: { 104 HandleNotification(label, 105 Packet::Specialize<RegisterNotificationRequest>(pkt)); 106 } break; 107 108 case CommandPdu::GET_ELEMENT_ATTRIBUTES: { 109 media_interface_->GetSongInfo(base::Bind( 110 &Device::GetElementAttributesResponse, weak_ptr_factory_.GetWeakPtr(), 111 label, Packet::Specialize<GetElementAttributesRequest>(pkt))); 112 } break; 113 114 case CommandPdu::GET_PLAY_STATUS: { 115 media_interface_->GetPlayStatus(base::Bind(&Device::GetPlayStatusResponse, 116 weak_ptr_factory_.GetWeakPtr(), 117 label)); 118 } break; 119 120 case CommandPdu::PLAY_ITEM: { 121 HandlePlayItem(label, Packet::Specialize<PlayItemRequest>(pkt)); 122 } break; 123 124 case CommandPdu::SET_ADDRESSED_PLAYER: { 125 // TODO (apanicke): Implement set addressed player. We don't need 126 // this currently since the current implementation only has one 127 // player and the player will never change, but we need it for a 128 // more complete implementation. 129 auto response = RejectBuilder::MakeBuilder( 130 CommandPdu::SET_ADDRESSED_PLAYER, Status::INVALID_PLAYER_ID); 131 send_message(label, false, std::move(response)); 132 } break; 133 134 default: { 135 DEVICE_LOG(ERROR) << "Unhandled Vendor Packet: " << pkt->ToString(); 136 auto response = RejectBuilder::MakeBuilder( 137 (CommandPdu)pkt->GetCommandPdu(), Status::INVALID_COMMAND); 138 send_message(label, false, std::move(response)); 139 } break; 140 } 141 } 142 143 void Device::HandleGetCapabilities( 144 uint8_t label, const std::shared_ptr<GetCapabilitiesRequest>& pkt) { 145 DEVICE_VLOG(4) << __func__ 146 << ": capability=" << pkt->GetCapabilityRequested(); 147 148 switch (pkt->GetCapabilityRequested()) { 149 case Capability::COMPANY_ID: { 150 auto response = 151 GetCapabilitiesResponseBuilder::MakeCompanyIdBuilder(0x001958); 152 response->AddCompanyId(0x002345); 153 send_message_cb_.Run(label, false, std::move(response)); 154 } break; 155 156 case Capability::EVENTS_SUPPORTED: { 157 auto response = 158 GetCapabilitiesResponseBuilder::MakeEventsSupportedBuilder( 159 Event::PLAYBACK_STATUS_CHANGED); 160 response->AddEvent(Event::TRACK_CHANGED); 161 response->AddEvent(Event::PLAYBACK_POS_CHANGED); 162 163 if (!avrcp13_compatibility_) { 164 response->AddEvent(Event::AVAILABLE_PLAYERS_CHANGED); 165 response->AddEvent(Event::ADDRESSED_PLAYER_CHANGED); 166 response->AddEvent(Event::UIDS_CHANGED); 167 response->AddEvent(Event::NOW_PLAYING_CONTENT_CHANGED); 168 } 169 170 send_message(label, false, std::move(response)); 171 } break; 172 173 default: { 174 DEVICE_LOG(WARNING) << "Unhandled Capability: " 175 << pkt->GetCapabilityRequested(); 176 auto response = RejectBuilder::MakeBuilder(CommandPdu::GET_CAPABILITIES, 177 Status::INVALID_PARAMETER); 178 send_message(label, false, std::move(response)); 179 } break; 180 } 181 } 182 183 void Device::HandleNotification( 184 uint8_t label, const std::shared_ptr<RegisterNotificationRequest>& pkt) { 185 DEVICE_VLOG(4) << __func__ << ": event=" << pkt->GetEventRegistered(); 186 187 switch (pkt->GetEventRegistered()) { 188 case Event::TRACK_CHANGED: { 189 track_changed_ = Notification(true, label); 190 media_interface_->GetNowPlayingList( 191 base::Bind(&Device::TrackChangedNotificationResponse, 192 weak_ptr_factory_.GetWeakPtr(), label, true)); 193 } break; 194 195 case Event::PLAYBACK_STATUS_CHANGED: { 196 play_status_changed_ = Notification(true, label); 197 media_interface_->GetPlayStatus( 198 base::Bind(&Device::PlaybackStatusNotificationResponse, 199 weak_ptr_factory_.GetWeakPtr(), label, true)); 200 } break; 201 202 case Event::PLAYBACK_POS_CHANGED: { 203 play_pos_changed_ = Notification(true, label); 204 play_pos_interval_ = pkt->GetInterval(); 205 media_interface_->GetPlayStatus( 206 base::Bind(&Device::PlaybackPosNotificationResponse, 207 weak_ptr_factory_.GetWeakPtr(), label, true)); 208 } break; 209 210 case Event::NOW_PLAYING_CONTENT_CHANGED: { 211 now_playing_changed_ = Notification(true, label); 212 media_interface_->GetNowPlayingList(base::Bind( 213 &Device::HandleNowPlayingNotificationResponse, 214 weak_ptr_factory_.GetWeakPtr(), now_playing_changed_.second, true)); 215 } break; 216 217 case Event::AVAILABLE_PLAYERS_CHANGED: { 218 // Respond immediately since this notification doesn't require any info 219 avail_players_changed_ = Notification(true, label); 220 auto response = 221 RegisterNotificationResponseBuilder::MakeAvailablePlayersBuilder( 222 true); 223 send_message(label, false, std::move(response)); 224 } break; 225 226 case Event::ADDRESSED_PLAYER_CHANGED: { 227 addr_player_changed_ = Notification(true, label); 228 media_interface_->GetMediaPlayerList( 229 base::Bind(&Device::AddressedPlayerNotificationResponse, 230 weak_ptr_factory_.GetWeakPtr(), label, true)); 231 } break; 232 233 case Event::UIDS_CHANGED: { 234 // Respond immediately since this notification doesn't require any info 235 uids_changed_ = Notification(true, label); 236 auto response = 237 RegisterNotificationResponseBuilder::MakeUidsChangedBuilder(true, 0); 238 send_message(label, false, std::move(response)); 239 } break; 240 241 default: { 242 DEVICE_LOG(ERROR) << __func__ << " : Unknown event registered. Event ID=" 243 << pkt->GetEventRegistered(); 244 auto response = RejectBuilder::MakeBuilder( 245 (CommandPdu)pkt->GetCommandPdu(), Status::INVALID_PARAMETER); 246 send_message(label, false, std::move(response)); 247 } break; 248 } 249 } 250 251 void Device::RegisterVolumeChanged() { 252 DEVICE_VLOG(2) << __func__; 253 if (volume_interface_ == nullptr) return; 254 255 auto request = 256 RegisterNotificationRequestBuilder::MakeBuilder(Event::VOLUME_CHANGED, 0); 257 258 // Find an open transaction label to prevent conflicts with other commands 259 // that are in flight. We can not use the reserved label while the 260 // notification hasn't been completed. 261 uint8_t label = MAX_TRANSACTION_LABEL; 262 for (uint8_t i = 0; i < MAX_TRANSACTION_LABEL; i++) { 263 if (active_labels_.find(i) == active_labels_.end()) { 264 active_labels_.insert(i); 265 label = i; 266 break; 267 } 268 } 269 270 if (label == MAX_TRANSACTION_LABEL) { 271 DEVICE_LOG(FATAL) 272 << __func__ 273 << ": Abandon all hope, something went catastrophically wrong"; 274 } 275 276 send_message_cb_.Run(label, false, std::move(request)); 277 } 278 279 void Device::HandleVolumeChanged( 280 uint8_t label, const std::shared_ptr<RegisterNotificationResponse>& pkt) { 281 DEVICE_VLOG(1) << __func__ << ": interim=" << pkt->IsInterim(); 282 if (volume_interface_ == nullptr) return; 283 284 if (pkt->GetCType() == CType::REJECTED) { 285 // Disable Absolute Volume 286 active_labels_.erase(label); 287 volume_interface_ = nullptr; 288 volume_ = VOL_REGISTRATION_FAILED; 289 return; 290 } 291 292 // We only update on interim and just re-register on changes. 293 if (!pkt->IsInterim()) { 294 active_labels_.erase(label); 295 RegisterVolumeChanged(); 296 return; 297 } 298 299 // Handle the first volume update. 300 if (volume_ == VOL_NOT_SUPPORTED) { 301 volume_ = pkt->GetVolume(); 302 volume_interface_->DeviceConnected( 303 GetAddress(), 304 base::Bind(&Device::SetVolume, weak_ptr_factory_.GetWeakPtr())); 305 306 // Ignore the returned volume in favor of the volume returned 307 // by the volume interface. 308 return; 309 } 310 311 if (!IsActive()) { 312 DEVICE_VLOG(3) << __func__ 313 << ": Ignoring volume changes from non active device"; 314 return; 315 } 316 317 volume_ = pkt->GetVolume(); 318 DEVICE_VLOG(1) << __func__ << ": Volume has changed to " << (uint32_t)volume_; 319 volume_interface_->SetVolume(volume_); 320 } 321 322 void Device::SetVolume(int8_t volume) { 323 // TODO (apanicke): Implement logic for Multi-AVRCP 324 DEVICE_VLOG(1) << __func__ << ": volume=" << (int)volume; 325 auto request = SetAbsoluteVolumeRequestBuilder::MakeBuilder(volume); 326 327 uint8_t label = MAX_TRANSACTION_LABEL; 328 for (uint8_t i = 0; i < MAX_TRANSACTION_LABEL; i++) { 329 if (active_labels_.find(i) == active_labels_.end()) { 330 active_labels_.insert(i); 331 label = i; 332 break; 333 } 334 } 335 336 volume_ = volume; 337 send_message_cb_.Run(label, false, std::move(request)); 338 } 339 340 void Device::TrackChangedNotificationResponse(uint8_t label, bool interim, 341 std::string curr_song_id, 342 std::vector<SongInfo> song_list) { 343 DEVICE_VLOG(1) << __func__; 344 uint64_t uid = 0; 345 346 if (!track_changed_.first) { 347 DEVICE_VLOG(0) << __func__ << ": Device not registered for update"; 348 return; 349 } 350 351 // Anytime we use the now playing list, update our map so that its always 352 // current 353 now_playing_ids_.clear(); 354 for (const SongInfo& song : song_list) { 355 now_playing_ids_.insert(song.media_id); 356 if (curr_song_id == song.media_id) { 357 DEVICE_VLOG(3) << __func__ << ": Found media ID match for " 358 << song.media_id; 359 uid = now_playing_ids_.get_uid(curr_song_id); 360 } 361 } 362 363 if (curr_song_id == "") { 364 DEVICE_LOG(WARNING) << "Empty media ID"; 365 uid = 0; 366 if (stack_config_get_interface()->get_pts_avrcp_test()) { 367 DEVICE_LOG(WARNING) << __func__ << ": pts test mode"; 368 uid = 0xffffffffffffffff; 369 } 370 } 371 372 auto response = RegisterNotificationResponseBuilder::MakeTrackChangedBuilder( 373 interim, uid); 374 send_message_cb_.Run(label, false, std::move(response)); 375 if (!interim) { 376 active_labels_.erase(label); 377 track_changed_ = Notification(false, 0); 378 } 379 } 380 381 void Device::PlaybackStatusNotificationResponse(uint8_t label, bool interim, 382 PlayStatus status) { 383 DEVICE_VLOG(1) << __func__; 384 if (status.state == PlayState::PAUSED) play_pos_update_cb_.Cancel(); 385 386 if (!play_status_changed_.first) { 387 DEVICE_VLOG(0) << __func__ << ": Device not registered for update"; 388 return; 389 } 390 391 auto state_to_send = status.state; 392 if (!IsActive()) state_to_send = PlayState::PAUSED; 393 if (!interim && state_to_send == last_play_status_.state) { 394 DEVICE_VLOG(0) << __func__ 395 << ": Not sending notification due to no state update " 396 << address_.ToString(); 397 return; 398 } 399 400 last_play_status_.state = state_to_send; 401 402 auto response = 403 RegisterNotificationResponseBuilder::MakePlaybackStatusBuilder( 404 interim, IsActive() ? status.state : PlayState::PAUSED); 405 send_message_cb_.Run(label, false, std::move(response)); 406 407 if (!interim) { 408 active_labels_.erase(label); 409 play_status_changed_ = Notification(false, 0); 410 } 411 } 412 413 void Device::PlaybackPosNotificationResponse(uint8_t label, bool interim, 414 PlayStatus status) { 415 DEVICE_VLOG(4) << __func__; 416 417 if (!play_pos_changed_.first) { 418 DEVICE_VLOG(3) << __func__ << ": Device not registered for update"; 419 return; 420 } 421 422 if (!interim && last_play_status_.position == status.position) { 423 DEVICE_LOG(WARNING) << address_.ToString() 424 << ": No update to play position"; 425 return; 426 } 427 428 auto response = 429 RegisterNotificationResponseBuilder::MakePlaybackPositionBuilder( 430 interim, status.position); 431 send_message_cb_.Run(label, false, std::move(response)); 432 433 last_play_status_.position = status.position; 434 435 if (!interim) { 436 active_labels_.erase(label); 437 play_pos_changed_ = Notification(false, 0); 438 } 439 440 // We still try to send updates while music is playing to the non active 441 // device even though the device thinks the music is paused. This makes 442 // the status bar on the remote device move. 443 if (status.state == PlayState::PLAYING) { 444 DEVICE_VLOG(0) << __func__ << ": Queue next play position update"; 445 play_pos_update_cb_.Reset(base::Bind(&Device::HandlePlayPosUpdate, 446 weak_ptr_factory_.GetWeakPtr())); 447 base::MessageLoop::current()->task_runner()->PostDelayedTask( 448 FROM_HERE, play_pos_update_cb_.callback(), 449 base::TimeDelta::FromSeconds(play_pos_interval_)); 450 } 451 } 452 453 // TODO (apanicke): Finish implementing when we add support for more than one 454 // player 455 void Device::AddressedPlayerNotificationResponse( 456 uint8_t label, bool interim, uint16_t curr_player, 457 std::vector<MediaPlayerInfo> /* unused */) { 458 DEVICE_VLOG(1) << __func__ 459 << ": curr_player_id=" << (unsigned int)curr_player; 460 // If there is no set browsed player, use the current addressed player as the 461 // default NOTE: Using any browsing commands before the browsed player is set 462 // is a violation of the AVRCP Spec but there are some carkits that try too 463 // anyways 464 if (curr_browsed_player_id_ == -1) curr_browsed_player_id_ = curr_player; 465 466 auto response = 467 RegisterNotificationResponseBuilder::MakeAddressedPlayerBuilder( 468 interim, curr_player, 0x0000); 469 send_message_cb_.Run(label, false, std::move(response)); 470 471 if (!interim) { 472 active_labels_.erase(label); 473 addr_player_changed_ = Notification(false, 0); 474 RejectNotification(); 475 } 476 } 477 478 void Device::RejectNotification() { 479 DEVICE_VLOG(1) << __func__; 480 Notification* rejectNotification[] = {&play_status_changed_, &track_changed_, 481 &play_pos_changed_, 482 &now_playing_changed_}; 483 for (int i = 0; i < 4; i++) { 484 uint8_t label = rejectNotification[i]->second; 485 auto response = RejectBuilder::MakeBuilder( 486 CommandPdu::REGISTER_NOTIFICATION, Status::ADDRESSED_PLAYER_CHANGED); 487 send_message_cb_.Run(label, false, std::move(response)); 488 active_labels_.erase(label); 489 rejectNotification[i] = new Notification(false, 0); 490 } 491 } 492 493 void Device::GetPlayStatusResponse(uint8_t label, PlayStatus status) { 494 DEVICE_VLOG(2) << __func__ << ": position=" << status.position 495 << " duration=" << status.duration 496 << " state=" << status.state; 497 auto response = GetPlayStatusResponseBuilder::MakeBuilder( 498 status.duration, status.position, 499 IsActive() ? status.state : PlayState::PAUSED); 500 send_message(label, false, std::move(response)); 501 } 502 503 void Device::GetElementAttributesResponse( 504 uint8_t label, std::shared_ptr<GetElementAttributesRequest> pkt, 505 SongInfo info) { 506 DEVICE_VLOG(2) << __func__; 507 auto get_element_attributes_pkt = pkt; 508 auto attributes_requested = 509 get_element_attributes_pkt->GetAttributesRequested(); 510 511 auto response = GetElementAttributesResponseBuilder::MakeBuilder(ctrl_mtu_); 512 513 last_song_info_ = info; 514 515 if (attributes_requested.size() != 0) { 516 for (const auto& attribute : attributes_requested) { 517 if (info.attributes.find(attribute) != info.attributes.end()) { 518 response->AddAttributeEntry(*info.attributes.find(attribute)); 519 } 520 } 521 } else { // zero attributes requested which means all attributes requested 522 for (const auto& attribute : info.attributes) { 523 response->AddAttributeEntry(attribute); 524 } 525 } 526 527 send_message(label, false, std::move(response)); 528 } 529 530 void Device::MessageReceived(uint8_t label, std::shared_ptr<Packet> pkt) { 531 DEVICE_VLOG(4) << __func__ << ": opcode=" << pkt->GetOpcode(); 532 533 active_labels_.insert(label); 534 535 switch (pkt->GetOpcode()) { 536 // TODO (apanicke): Remove handling of UNIT_INFO and SUBUNIT_INFO from 537 // the AVRC_API and instead handle it here to reduce fragmentation. 538 case Opcode::UNIT_INFO: { 539 } break; 540 case Opcode::SUBUNIT_INFO: { 541 } break; 542 case Opcode::PASS_THROUGH: { 543 auto pass_through_packet = Packet::Specialize<PassThroughPacket>(pkt); 544 auto response = PassThroughPacketBuilder::MakeBuilder( 545 true, pass_through_packet->GetKeyState() == KeyState::PUSHED, 546 pass_through_packet->GetOperationId()); 547 send_message(label, false, std::move(response)); 548 549 // TODO (apanicke): Use an enum for media key ID's 550 if (pass_through_packet->GetOperationId() == 0x44 && 551 pass_through_packet->GetKeyState() == KeyState::PUSHED) { 552 // We need to get the play status since we need to know 553 // what the actual playstate is without being modified 554 // by whether the device is active. 555 media_interface_->GetPlayStatus(base::Bind( 556 [](base::WeakPtr<Device> d, PlayStatus s) { 557 if (!d) return; 558 559 if (!d->IsActive()) { 560 LOG(INFO) << "Setting " << d->address_.ToString() 561 << " to be the active device"; 562 d->media_interface_->SetActiveDevice(d->address_); 563 564 if (s.state == PlayState::PLAYING) { 565 LOG(INFO) 566 << "Skipping sendKeyEvent since music is already playing"; 567 return; 568 } 569 } 570 571 d->media_interface_->SendKeyEvent(0x44, KeyState::PUSHED); 572 }, 573 weak_ptr_factory_.GetWeakPtr())); 574 return; 575 } 576 577 if (IsActive()) { 578 media_interface_->SendKeyEvent(pass_through_packet->GetOperationId(), 579 pass_through_packet->GetKeyState()); 580 } 581 } break; 582 case Opcode::VENDOR: { 583 auto vendor_pkt = Packet::Specialize<VendorPacket>(pkt); 584 VendorPacketHandler(label, vendor_pkt); 585 } break; 586 } 587 } 588 589 void Device::HandlePlayItem(uint8_t label, 590 std::shared_ptr<PlayItemRequest> pkt) { 591 DEVICE_VLOG(2) << __func__ << ": scope=" << pkt->GetScope() 592 << " uid=" << pkt->GetUid(); 593 594 std::string media_id = ""; 595 switch (pkt->GetScope()) { 596 case Scope::NOW_PLAYING: 597 media_id = now_playing_ids_.get_media_id(pkt->GetUid()); 598 break; 599 case Scope::VFS: 600 media_id = vfs_ids_.get_media_id(pkt->GetUid()); 601 break; 602 default: 603 DEVICE_LOG(WARNING) << __func__ << ": Unknown scope for play item"; 604 } 605 606 if (media_id == "") { 607 DEVICE_VLOG(2) << "Could not find item"; 608 auto response = RejectBuilder::MakeBuilder(CommandPdu::PLAY_ITEM, 609 Status::DOES_NOT_EXIST); 610 send_message(label, false, std::move(response)); 611 return; 612 } 613 614 media_interface_->PlayItem(curr_browsed_player_id_, 615 pkt->GetScope() == Scope::NOW_PLAYING, media_id); 616 617 auto response = PlayItemResponseBuilder::MakeBuilder(Status::NO_ERROR); 618 send_message(label, false, std::move(response)); 619 } 620 621 void Device::BrowseMessageReceived(uint8_t label, 622 std::shared_ptr<BrowsePacket> pkt) { 623 DEVICE_VLOG(1) << __func__ << ": pdu=" << pkt->GetPdu(); 624 625 switch (pkt->GetPdu()) { 626 case BrowsePdu::SET_BROWSED_PLAYER: 627 HandleSetBrowsedPlayer(label, 628 Packet::Specialize<SetBrowsedPlayerRequest>(pkt)); 629 break; 630 case BrowsePdu::GET_FOLDER_ITEMS: 631 HandleGetFolderItems(label, 632 Packet::Specialize<GetFolderItemsRequest>(pkt)); 633 break; 634 case BrowsePdu::CHANGE_PATH: 635 HandleChangePath(label, Packet::Specialize<ChangePathRequest>(pkt)); 636 break; 637 case BrowsePdu::GET_ITEM_ATTRIBUTES: 638 HandleGetItemAttributes( 639 label, Packet::Specialize<GetItemAttributesRequest>(pkt)); 640 break; 641 case BrowsePdu::GET_TOTAL_NUMBER_OF_ITEMS: 642 HandleGetTotalNumberOfItems( 643 label, Packet::Specialize<GetTotalNumberOfItemsRequest>(pkt)); 644 break; 645 default: 646 DEVICE_LOG(WARNING) << __func__ << ": " << pkt->GetPdu(); 647 auto response = GeneralRejectBuilder::MakeBuilder( 648 BrowsePdu::GENERAL_REJECT, Status::INVALID_COMMAND); 649 send_message(label, true, std::move(response)); 650 651 break; 652 } 653 } 654 655 void Device::HandleGetFolderItems(uint8_t label, 656 std::shared_ptr<GetFolderItemsRequest> pkt) { 657 DEVICE_VLOG(2) << __func__ << ": scope=" << pkt->GetScope(); 658 659 switch (pkt->GetScope()) { 660 case Scope::MEDIA_PLAYER_LIST: 661 media_interface_->GetMediaPlayerList( 662 base::Bind(&Device::GetMediaPlayerListResponse, 663 weak_ptr_factory_.GetWeakPtr(), label, pkt)); 664 break; 665 case Scope::VFS: 666 media_interface_->GetFolderItems( 667 curr_browsed_player_id_, CurrentFolder(), 668 base::Bind(&Device::GetVFSListResponse, 669 weak_ptr_factory_.GetWeakPtr(), label, pkt)); 670 break; 671 case Scope::NOW_PLAYING: 672 media_interface_->GetNowPlayingList( 673 base::Bind(&Device::GetNowPlayingListResponse, 674 weak_ptr_factory_.GetWeakPtr(), label, pkt)); 675 break; 676 default: 677 DEVICE_LOG(ERROR) << __func__ << ": " << pkt->GetScope(); 678 break; 679 } 680 } 681 682 void Device::HandleGetTotalNumberOfItems( 683 uint8_t label, std::shared_ptr<GetTotalNumberOfItemsRequest> pkt) { 684 DEVICE_VLOG(2) << __func__ << ": scope=" << pkt->GetScope(); 685 686 switch (pkt->GetScope()) { 687 case Scope::MEDIA_PLAYER_LIST: { 688 media_interface_->GetMediaPlayerList( 689 base::Bind(&Device::GetTotalNumberOfItemsMediaPlayersResponse, 690 weak_ptr_factory_.GetWeakPtr(), label)); 691 break; 692 } 693 case Scope::VFS: 694 media_interface_->GetFolderItems( 695 curr_browsed_player_id_, CurrentFolder(), 696 base::Bind(&Device::GetTotalNumberOfItemsVFSResponse, 697 weak_ptr_factory_.GetWeakPtr(), label)); 698 break; 699 case Scope::NOW_PLAYING: 700 media_interface_->GetNowPlayingList( 701 base::Bind(&Device::GetTotalNumberOfItemsNowPlayingResponse, 702 weak_ptr_factory_.GetWeakPtr(), label)); 703 break; 704 default: 705 DEVICE_LOG(ERROR) << __func__ << ": " << pkt->GetScope(); 706 break; 707 } 708 } 709 710 void Device::GetTotalNumberOfItemsMediaPlayersResponse( 711 uint8_t label, uint16_t curr_player, std::vector<MediaPlayerInfo> list) { 712 DEVICE_VLOG(2) << __func__ << ": num_items=" << list.size(); 713 714 auto builder = GetTotalNumberOfItemsResponseBuilder::MakeBuilder( 715 Status::NO_ERROR, 0x0000, list.size()); 716 send_message(label, true, std::move(builder)); 717 } 718 719 void Device::GetTotalNumberOfItemsVFSResponse(uint8_t label, 720 std::vector<ListItem> list) { 721 DEVICE_VLOG(2) << __func__ << ": num_items=" << list.size(); 722 723 auto builder = GetTotalNumberOfItemsResponseBuilder::MakeBuilder( 724 Status::NO_ERROR, 0x0000, list.size()); 725 send_message(label, true, std::move(builder)); 726 } 727 728 void Device::GetTotalNumberOfItemsNowPlayingResponse( 729 uint8_t label, std::string curr_song_id, std::vector<SongInfo> list) { 730 DEVICE_VLOG(2) << __func__ << ": num_items=" << list.size(); 731 732 auto builder = GetTotalNumberOfItemsResponseBuilder::MakeBuilder( 733 Status::NO_ERROR, 0x0000, list.size()); 734 send_message(label, true, std::move(builder)); 735 } 736 737 void Device::HandleChangePath(uint8_t label, 738 std::shared_ptr<ChangePathRequest> pkt) { 739 DEVICE_VLOG(2) << __func__ << ": direction=" << pkt->GetDirection() 740 << " uid=" << loghex(pkt->GetUid()); 741 742 if (pkt->GetDirection() == Direction::DOWN && 743 vfs_ids_.get_media_id(pkt->GetUid()) == "") { 744 DEVICE_LOG(ERROR) << __func__ 745 << ": No item found for UID=" << pkt->GetUid(); 746 auto builder = 747 ChangePathResponseBuilder::MakeBuilder(Status::DOES_NOT_EXIST, 0); 748 send_message(label, true, std::move(builder)); 749 return; 750 } 751 752 if (pkt->GetDirection() == Direction::DOWN) { 753 current_path_.push(vfs_ids_.get_media_id(pkt->GetUid())); 754 DEVICE_VLOG(2) << "Pushing Path to stack: \"" << CurrentFolder() << "\""; 755 } else { 756 // Don't pop the root id off the stack 757 if (current_path_.size() > 1) { 758 current_path_.pop(); 759 } else { 760 DEVICE_LOG(ERROR) << "Trying to change directory up past root."; 761 auto builder = 762 ChangePathResponseBuilder::MakeBuilder(Status::DOES_NOT_EXIST, 0); 763 send_message(label, true, std::move(builder)); 764 return; 765 } 766 767 DEVICE_VLOG(2) << "Popping Path from stack: new path=\"" << CurrentFolder() 768 << "\""; 769 } 770 771 media_interface_->GetFolderItems( 772 curr_browsed_player_id_, CurrentFolder(), 773 base::Bind(&Device::ChangePathResponse, weak_ptr_factory_.GetWeakPtr(), 774 label, pkt)); 775 } 776 777 void Device::ChangePathResponse(uint8_t label, 778 std::shared_ptr<ChangePathRequest> pkt, 779 std::vector<ListItem> list) { 780 // TODO (apanicke): Reconstruct the VFS ID's here. Right now it gets 781 // reconstructed in GetFolderItemsVFS 782 auto builder = 783 ChangePathResponseBuilder::MakeBuilder(Status::NO_ERROR, list.size()); 784 send_message(label, true, std::move(builder)); 785 } 786 787 void Device::HandleGetItemAttributes( 788 uint8_t label, std::shared_ptr<GetItemAttributesRequest> pkt) { 789 DEVICE_VLOG(2) << __func__ << ": scope=" << pkt->GetScope() 790 << " uid=" << loghex(pkt->GetUid()) 791 << " uid counter=" << loghex(pkt->GetUidCounter()); 792 if (pkt->GetUidCounter() != 0x0000) { // For database unaware player, use 0 793 DEVICE_LOG(WARNING) << "UidCounter is invalid"; 794 auto builder = GetItemAttributesResponseBuilder::MakeBuilder( 795 Status::UIDS_CHANGED, browse_mtu_); 796 send_message(label, true, std::move(builder)); 797 return; 798 } 799 switch (pkt->GetScope()) { 800 case Scope::NOW_PLAYING: { 801 media_interface_->GetNowPlayingList( 802 base::Bind(&Device::GetItemAttributesNowPlayingResponse, 803 weak_ptr_factory_.GetWeakPtr(), label, pkt)); 804 } break; 805 case Scope::VFS: 806 // TODO (apanicke): Check the vfs_ids_ here. If the item doesn't exist 807 // then we can auto send the error without calling up. We do this check 808 // later right now though in order to prevent race conditions with updates 809 // on the media layer. 810 media_interface_->GetFolderItems( 811 curr_browsed_player_id_, CurrentFolder(), 812 base::Bind(&Device::GetItemAttributesVFSResponse, 813 weak_ptr_factory_.GetWeakPtr(), label, pkt)); 814 break; 815 default: 816 DEVICE_LOG(ERROR) << "UNKNOWN SCOPE FOR HANDLE GET ITEM ATTRIBUTES"; 817 break; 818 } 819 } 820 821 void Device::GetItemAttributesNowPlayingResponse( 822 uint8_t label, std::shared_ptr<GetItemAttributesRequest> pkt, 823 std::string curr_media_id, std::vector<SongInfo> song_list) { 824 DEVICE_VLOG(2) << __func__ << ": uid=" << loghex(pkt->GetUid()); 825 auto builder = GetItemAttributesResponseBuilder::MakeBuilder(Status::NO_ERROR, 826 browse_mtu_); 827 828 auto media_id = now_playing_ids_.get_media_id(pkt->GetUid()); 829 if (media_id == "") { 830 media_id = curr_media_id; 831 } 832 833 DEVICE_VLOG(2) << __func__ << ": media_id=\"" << media_id << "\""; 834 835 SongInfo info; 836 for (const auto& temp : song_list) { 837 if (temp.media_id == media_id) { 838 info = temp; 839 } 840 } 841 842 auto attributes_requested = pkt->GetAttributesRequested(); 843 if (attributes_requested.size() != 0) { 844 for (const auto& attribute : attributes_requested) { 845 if (info.attributes.find(attribute) != info.attributes.end()) { 846 builder->AddAttributeEntry(*info.attributes.find(attribute)); 847 } 848 } 849 } else { 850 // If zero attributes were requested, that means all attributes were 851 // requested 852 for (const auto& attribute : info.attributes) { 853 builder->AddAttributeEntry(attribute); 854 } 855 } 856 857 send_message(label, true, std::move(builder)); 858 } 859 860 void Device::GetItemAttributesVFSResponse( 861 uint8_t label, std::shared_ptr<GetItemAttributesRequest> pkt, 862 std::vector<ListItem> item_list) { 863 DEVICE_VLOG(2) << __func__ << ": uid=" << loghex(pkt->GetUid()); 864 865 auto media_id = vfs_ids_.get_media_id(pkt->GetUid()); 866 if (media_id == "") { 867 LOG(WARNING) << __func__ << ": Item not found"; 868 auto builder = GetItemAttributesResponseBuilder::MakeBuilder( 869 Status::DOES_NOT_EXIST, browse_mtu_); 870 send_message(label, true, std::move(builder)); 871 return; 872 } 873 874 auto builder = GetItemAttributesResponseBuilder::MakeBuilder(Status::NO_ERROR, 875 browse_mtu_); 876 877 ListItem item_requested; 878 for (const auto& temp : item_list) { 879 if ((temp.type == ListItem::FOLDER && temp.folder.media_id == media_id) || 880 (temp.type == ListItem::SONG && temp.song.media_id == media_id)) { 881 item_requested = temp; 882 } 883 } 884 885 // TODO (apanicke): Add a helper function or allow adding a map 886 // of attributes to GetItemAttributesResponseBuilder 887 auto attributes_requested = pkt->GetAttributesRequested(); 888 if (item_requested.type == ListItem::FOLDER) { 889 if (attributes_requested.size() == 0) { 890 builder->AddAttributeEntry(Attribute::TITLE, item_requested.folder.name); 891 } else { 892 for (auto& attr : attributes_requested) { 893 if (attr == Attribute::TITLE) { 894 builder->AddAttributeEntry(Attribute::TITLE, 895 item_requested.folder.name); 896 } 897 } 898 } 899 } else { 900 if (attributes_requested.size() != 0) { 901 for (const auto& attribute : attributes_requested) { 902 if (item_requested.song.attributes.find(attribute) != 903 item_requested.song.attributes.end()) { 904 builder->AddAttributeEntry( 905 *item_requested.song.attributes.find(attribute)); 906 } 907 } 908 } else { 909 // If zero attributes were requested, that means all attributes were 910 // requested 911 for (const auto& attribute : item_requested.song.attributes) { 912 builder->AddAttributeEntry(attribute); 913 } 914 } 915 } 916 917 send_message(label, true, std::move(builder)); 918 } 919 920 void Device::GetMediaPlayerListResponse( 921 uint8_t label, std::shared_ptr<GetFolderItemsRequest> pkt, 922 uint16_t curr_player, std::vector<MediaPlayerInfo> players) { 923 DEVICE_VLOG(2) << __func__; 924 925 if (players.size() == 0) { 926 auto no_items_rsp = GetFolderItemsResponseBuilder::MakePlayerListBuilder( 927 Status::RANGE_OUT_OF_BOUNDS, 0x0000, browse_mtu_); 928 send_message(label, true, std::move(no_items_rsp)); 929 } 930 931 auto builder = GetFolderItemsResponseBuilder::MakePlayerListBuilder( 932 Status::NO_ERROR, 0x0000, browse_mtu_); 933 934 // Move the current player to the first slot due to some carkits always 935 // connecting to the first listed player rather than using the ID 936 // returned by Addressed Player Changed 937 for (auto it = players.begin(); it != players.end(); it++) { 938 if (it->id == curr_player) { 939 DEVICE_VLOG(1) << " Adding player to first spot: " << it->name; 940 auto temp_player = *it; 941 players.erase(it); 942 players.insert(players.begin(), temp_player); 943 break; 944 } 945 } 946 947 for (size_t i = pkt->GetStartItem(); 948 i <= pkt->GetEndItem() && i < players.size(); i++) { 949 MediaPlayerItem item(players[i].id, players[i].name, 950 players[i].browsing_supported); 951 builder->AddMediaPlayer(item); 952 } 953 954 send_message(label, true, std::move(builder)); 955 } 956 957 std::set<AttributeEntry> filter_attributes_requested( 958 const SongInfo& song, const std::vector<Attribute>& attrs) { 959 std::set<AttributeEntry> result; 960 for (const auto& attr : attrs) { 961 if (song.attributes.find(attr) != song.attributes.end()) { 962 result.insert(*song.attributes.find(attr)); 963 } 964 } 965 966 return result; 967 } 968 969 void Device::GetVFSListResponse(uint8_t label, 970 std::shared_ptr<GetFolderItemsRequest> pkt, 971 std::vector<ListItem> items) { 972 DEVICE_VLOG(2) << __func__ << ": start_item=" << pkt->GetStartItem() 973 << " end_item=" << pkt->GetEndItem(); 974 975 // The builder will automatically correct the status if there are zero items 976 auto builder = GetFolderItemsResponseBuilder::MakeVFSBuilder( 977 Status::NO_ERROR, 0x0000, browse_mtu_); 978 979 // TODO (apanicke): Add test that checks if vfs_ids_ is the correct size after 980 // an operation. 981 for (const auto& item : items) { 982 if (item.type == ListItem::FOLDER) { 983 vfs_ids_.insert(item.folder.media_id); 984 } else if (item.type == ListItem::SONG) { 985 vfs_ids_.insert(item.song.media_id); 986 } 987 } 988 989 // Add the elements retrieved in the last get folder items request and map 990 // them to UIDs The maps will be cleared every time a directory change 991 // happens. These items do not need to correspond with the now playing list as 992 // the UID's only need to be unique in the context of the current scope and 993 // the current folder 994 for (auto i = pkt->GetStartItem(); i <= pkt->GetEndItem() && i < items.size(); 995 i++) { 996 if (items[i].type == ListItem::FOLDER) { 997 auto folder = items[i].folder; 998 // right now we always use folders of mixed type 999 FolderItem folder_item(vfs_ids_.get_uid(folder.media_id), 0x00, 1000 folder.is_playable, folder.name); 1001 builder->AddFolder(folder_item); 1002 } else if (items[i].type == ListItem::SONG) { 1003 auto song = items[i].song; 1004 auto title = 1005 song.attributes.find(Attribute::TITLE) != song.attributes.end() 1006 ? song.attributes.find(Attribute::TITLE)->value() 1007 : "No Song Info"; 1008 MediaElementItem song_item(vfs_ids_.get_uid(song.media_id), title, 1009 std::set<AttributeEntry>()); 1010 1011 if (pkt->GetNumAttributes() == 0x00) { // All attributes requested 1012 song_item.attributes_ = std::move(song.attributes); 1013 } else { 1014 song_item.attributes_ = 1015 filter_attributes_requested(song, pkt->GetAttributesRequested()); 1016 } 1017 1018 builder->AddSong(song_item); 1019 } 1020 } 1021 1022 send_message(label, true, std::move(builder)); 1023 } 1024 1025 void Device::GetNowPlayingListResponse( 1026 uint8_t label, std::shared_ptr<GetFolderItemsRequest> pkt, 1027 std::string /* unused curr_song_id */, std::vector<SongInfo> song_list) { 1028 DEVICE_VLOG(2) << __func__; 1029 auto builder = GetFolderItemsResponseBuilder::MakeNowPlayingBuilder( 1030 Status::NO_ERROR, 0x0000, browse_mtu_); 1031 1032 now_playing_ids_.clear(); 1033 for (const SongInfo& song : song_list) { 1034 now_playing_ids_.insert(song.media_id); 1035 } 1036 1037 for (size_t i = pkt->GetStartItem(); 1038 i <= pkt->GetEndItem() && i < song_list.size(); i++) { 1039 auto song = song_list[i]; 1040 auto title = song.attributes.find(Attribute::TITLE) != song.attributes.end() 1041 ? song.attributes.find(Attribute::TITLE)->value() 1042 : "No Song Info"; 1043 1044 MediaElementItem item(i + 1, title, std::set<AttributeEntry>()); 1045 if (pkt->GetNumAttributes() == 0x00) { 1046 item.attributes_ = std::move(song.attributes); 1047 } else { 1048 item.attributes_ = 1049 filter_attributes_requested(song, pkt->GetAttributesRequested()); 1050 } 1051 builder->AddSong(item); 1052 } 1053 1054 send_message(label, true, std::move(builder)); 1055 } 1056 1057 void Device::HandleSetBrowsedPlayer( 1058 uint8_t label, std::shared_ptr<SetBrowsedPlayerRequest> pkt) { 1059 DEVICE_VLOG(2) << __func__ << ": player_id=" << pkt->GetPlayerId(); 1060 media_interface_->SetBrowsedPlayer( 1061 pkt->GetPlayerId(), 1062 base::Bind(&Device::SetBrowsedPlayerResponse, 1063 weak_ptr_factory_.GetWeakPtr(), label, pkt)); 1064 } 1065 1066 void Device::SetBrowsedPlayerResponse( 1067 uint8_t label, std::shared_ptr<SetBrowsedPlayerRequest> pkt, bool success, 1068 std::string root_id, uint32_t num_items) { 1069 DEVICE_VLOG(2) << __func__ << ": success=" << success << " root_id=\"" 1070 << root_id << "\" num_items=" << num_items; 1071 1072 if (!success) { 1073 auto response = SetBrowsedPlayerResponseBuilder::MakeBuilder( 1074 Status::INVALID_PLAYER_ID, 0x0000, num_items, 0, ""); 1075 send_message(label, true, std::move(response)); 1076 return; 1077 } 1078 1079 curr_browsed_player_id_ = pkt->GetPlayerId(); 1080 1081 // Clear the path and push the new root. 1082 current_path_ = std::stack<std::string>(); 1083 current_path_.push(root_id); 1084 1085 auto response = SetBrowsedPlayerResponseBuilder::MakeBuilder( 1086 Status::NO_ERROR, 0x0000, num_items, 0, ""); 1087 send_message(label, true, std::move(response)); 1088 } 1089 1090 void Device::SendMediaUpdate(bool metadata, bool play_status, bool queue) { 1091 CHECK(media_interface_); 1092 DEVICE_VLOG(4) << __func__ << ": Metadata=" << metadata 1093 << " : play_status= " << play_status << " : queue=" << queue; 1094 1095 if (queue) { 1096 HandleNowPlayingUpdate(); 1097 } 1098 1099 if (play_status) { 1100 HandlePlayStatusUpdate(); 1101 HandlePlayPosUpdate(); 1102 } 1103 1104 if (metadata) HandleTrackUpdate(); 1105 } 1106 1107 void Device::SendFolderUpdate(bool available_players, bool addressed_player, 1108 bool uids) { 1109 CHECK(media_interface_); 1110 DEVICE_VLOG(4) << __func__; 1111 1112 if (available_players) { 1113 HandleAvailablePlayerUpdate(); 1114 } 1115 1116 if (addressed_player) { 1117 HandleAddressedPlayerUpdate(); 1118 } 1119 } 1120 1121 void Device::HandleTrackUpdate() { 1122 DEVICE_VLOG(2) << __func__; 1123 if (!track_changed_.first) { 1124 LOG(WARNING) << "Device is not registered for track changed updates"; 1125 return; 1126 } 1127 1128 media_interface_->GetNowPlayingList( 1129 base::Bind(&Device::TrackChangedNotificationResponse, 1130 weak_ptr_factory_.GetWeakPtr(), track_changed_.second, false)); 1131 } 1132 1133 void Device::HandlePlayStatusUpdate() { 1134 DEVICE_VLOG(2) << __func__; 1135 if (!play_status_changed_.first) { 1136 LOG(WARNING) << "Device is not registered for play status updates"; 1137 return; 1138 } 1139 1140 media_interface_->GetPlayStatus(base::Bind( 1141 &Device::PlaybackStatusNotificationResponse, 1142 weak_ptr_factory_.GetWeakPtr(), play_status_changed_.second, false)); 1143 } 1144 1145 void Device::HandleNowPlayingUpdate() { 1146 DEVICE_VLOG(2) << __func__; 1147 1148 if (!now_playing_changed_.first) { 1149 LOG(WARNING) << "Device is not registered for now playing updates"; 1150 return; 1151 } 1152 1153 media_interface_->GetNowPlayingList(base::Bind( 1154 &Device::HandleNowPlayingNotificationResponse, 1155 weak_ptr_factory_.GetWeakPtr(), now_playing_changed_.second, false)); 1156 } 1157 1158 void Device::HandleNowPlayingNotificationResponse( 1159 uint8_t label, bool interim, std::string curr_song_id, 1160 std::vector<SongInfo> song_list) { 1161 if (!now_playing_changed_.first) { 1162 LOG(WARNING) << "Device is not registered for now playing updates"; 1163 return; 1164 } 1165 1166 now_playing_ids_.clear(); 1167 for (const SongInfo& song : song_list) { 1168 now_playing_ids_.insert(song.media_id); 1169 } 1170 1171 auto response = 1172 RegisterNotificationResponseBuilder::MakeNowPlayingBuilder(interim); 1173 send_message(now_playing_changed_.second, false, std::move(response)); 1174 1175 if (!interim) { 1176 active_labels_.erase(label); 1177 now_playing_changed_ = Notification(false, 0); 1178 } 1179 } 1180 1181 void Device::HandlePlayPosUpdate() { 1182 DEVICE_VLOG(0) << __func__; 1183 if (!play_pos_changed_.first) { 1184 LOG(WARNING) << "Device is not registered for play position updates"; 1185 return; 1186 } 1187 1188 media_interface_->GetPlayStatus(base::Bind( 1189 &Device::PlaybackPosNotificationResponse, weak_ptr_factory_.GetWeakPtr(), 1190 play_pos_changed_.second, false)); 1191 } 1192 1193 void Device::HandleAvailablePlayerUpdate() { 1194 DEVICE_VLOG(1) << __func__; 1195 1196 if (!avail_players_changed_.first) { 1197 LOG(WARNING) << "Device is not registered for available player updates"; 1198 return; 1199 } 1200 1201 auto response = 1202 RegisterNotificationResponseBuilder::MakeAvailablePlayersBuilder(false); 1203 send_message_cb_.Run(avail_players_changed_.second, false, 1204 std::move(response)); 1205 1206 if (!avail_players_changed_.first) { 1207 active_labels_.erase(avail_players_changed_.second); 1208 avail_players_changed_ = Notification(false, 0); 1209 } 1210 } 1211 1212 void Device::HandleAddressedPlayerUpdate() { 1213 DEVICE_VLOG(1) << __func__; 1214 if (!addr_player_changed_.first) { 1215 DEVICE_LOG(WARNING) 1216 << "Device is not registered for addressed player updates"; 1217 return; 1218 } 1219 media_interface_->GetMediaPlayerList(base::Bind( 1220 &Device::AddressedPlayerNotificationResponse, 1221 weak_ptr_factory_.GetWeakPtr(), addr_player_changed_.second, false)); 1222 } 1223 1224 void Device::DeviceDisconnected() { 1225 DEVICE_LOG(INFO) << "Device was disconnected"; 1226 play_pos_update_cb_.Cancel(); 1227 1228 // TODO (apanicke): Once the interfaces are set in the Device construction, 1229 // remove these conditionals. 1230 if (volume_interface_ != nullptr) 1231 volume_interface_->DeviceDisconnected(GetAddress()); 1232 } 1233 1234 static std::string volumeToStr(int8_t volume) { 1235 if (volume == VOL_NOT_SUPPORTED) return "Absolute Volume not supported"; 1236 if (volume == VOL_REGISTRATION_FAILED) 1237 return "Volume changed notification was rejected"; 1238 return std::to_string(volume); 1239 } 1240 1241 std::ostream& operator<<(std::ostream& out, const Device& d) { 1242 out << " " << d.address_.ToString(); 1243 if (d.IsActive()) out << " <Active>"; 1244 out << std::endl; 1245 out << " Current Volume: " << volumeToStr(d.volume_) << std::endl; 1246 out << " Current Browsed Player ID: " << d.curr_browsed_player_id_ 1247 << std::endl; 1248 out << " Registered Notifications: " << std::endl; 1249 if (d.track_changed_.first) { 1250 out << " Track Changed" << std::endl; 1251 } 1252 if (d.play_status_changed_.first) { 1253 out << " Play Status" << std::endl; 1254 } 1255 if (d.play_pos_changed_.first) { 1256 out << " Play Position" << std::endl; 1257 } 1258 if (d.now_playing_changed_.first) { 1259 out << " Now Playing" << std::endl; 1260 } 1261 if (d.addr_player_changed_.first) { 1262 out << " Addressed Player" << std::endl; 1263 } 1264 if (d.avail_players_changed_.first) { 1265 out << " Available Players" << std::endl; 1266 } 1267 if (d.uids_changed_.first) { 1268 out << " UIDs Changed" << std::endl; 1269 } 1270 1271 out << " Last Play State: " << d.last_play_status_.state << std::endl; 1272 out << " Last Song Sent ID: \"" << d.last_song_info_.media_id << "\"" 1273 << std::endl; 1274 out << " Current Folder: \"" << d.CurrentFolder() << "\"" << std::endl; 1275 out << " MTU Sizes: CTRL=" << d.ctrl_mtu_ << " BROWSE=" << d.browse_mtu_ 1276 << std::endl; 1277 // TODO (apanicke): Add supported features as well as media keys 1278 return out; 1279 } 1280 1281 } // namespace avrcp 1282 } // namespace bluetooth 1283