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