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