Home | History | Annotate | Download | only in avrcp
      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