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   auto request = SetAbsoluteVolumeRequestBuilder::MakeBuilder(volume);
415 
416   uint8_t label = MAX_TRANSACTION_LABEL;
417   for (uint8_t i = 0; i < MAX_TRANSACTION_LABEL; i++) {
418     if (active_labels_.find(i) == active_labels_.end()) {
419       active_labels_.insert(i);
420       label = i;
421       break;
422     }
423   }
424 
425   volume_ = volume;
426   send_message_cb_.Run(label, false, std::move(request));
427 }
428 
TrackChangedNotificationResponse(uint8_t label,bool interim,std::string curr_song_id,std::vector<SongInfo> song_list)429 void Device::TrackChangedNotificationResponse(uint8_t label, bool interim,
430                                               std::string curr_song_id,
431                                               std::vector<SongInfo> song_list) {
432   DEVICE_VLOG(1) << __func__;
433   uint64_t uid = 0;
434 
435   if (interim) {
436     track_changed_ = Notification(true, label);
437   } else if (!track_changed_.first) {
438     DEVICE_VLOG(0) << __func__ << ": Device not registered for update";
439     return;
440   }
441 
442   // Anytime we use the now playing list, update our map so that its always
443   // current
444   now_playing_ids_.clear();
445   for (const SongInfo& song : song_list) {
446     now_playing_ids_.insert(song.media_id);
447     if (curr_song_id == song.media_id) {
448       DEVICE_VLOG(3) << __func__ << ": Found media ID match for "
449                      << song.media_id;
450       uid = now_playing_ids_.get_uid(curr_song_id);
451     }
452   }
453 
454   if (curr_song_id == "") {
455     DEVICE_LOG(WARNING) << "Empty media ID";
456     uid = 0;
457     if (stack_config_get_interface()->get_pts_avrcp_test()) {
458       DEVICE_LOG(WARNING) << __func__ << ": pts test mode";
459       uid = 0xffffffffffffffff;
460     }
461   }
462 
463   auto response = RegisterNotificationResponseBuilder::MakeTrackChangedBuilder(
464       interim, uid);
465   send_message_cb_.Run(label, false, std::move(response));
466   if (!interim) {
467     active_labels_.erase(label);
468     track_changed_ = Notification(false, 0);
469   }
470 }
471 
PlaybackStatusNotificationResponse(uint8_t label,bool interim,PlayStatus status)472 void Device::PlaybackStatusNotificationResponse(uint8_t label, bool interim,
473                                                 PlayStatus status) {
474   DEVICE_VLOG(1) << __func__;
475   if (status.state == PlayState::PAUSED) play_pos_update_cb_.Cancel();
476 
477   if (interim) {
478     play_status_changed_ = Notification(true, label);
479   } else if (!play_status_changed_.first) {
480     DEVICE_VLOG(0) << __func__ << ": Device not registered for update";
481     return;
482   }
483 
484   auto state_to_send = status.state;
485   if (!IsActive()) state_to_send = PlayState::PAUSED;
486   if (!interim && state_to_send == last_play_status_.state) {
487     DEVICE_VLOG(0) << __func__
488                    << ": Not sending notification due to no state update "
489                    << address_.ToString();
490     return;
491   }
492 
493   last_play_status_.state = state_to_send;
494 
495   auto response =
496       RegisterNotificationResponseBuilder::MakePlaybackStatusBuilder(
497           interim, IsActive() ? status.state : PlayState::PAUSED);
498   send_message_cb_.Run(label, false, std::move(response));
499 
500   if (!interim) {
501     active_labels_.erase(label);
502     play_status_changed_ = Notification(false, 0);
503   }
504 }
505 
PlaybackPosNotificationResponse(uint8_t label,bool interim,PlayStatus status)506 void Device::PlaybackPosNotificationResponse(uint8_t label, bool interim,
507                                              PlayStatus status) {
508   DEVICE_VLOG(4) << __func__;
509 
510   if (interim) {
511     play_pos_changed_ = Notification(true, label);
512   } else if (!play_pos_changed_.first) {
513     DEVICE_VLOG(3) << __func__ << ": Device not registered for update";
514     return;
515   }
516 
517   if (!interim && last_play_status_.position == status.position) {
518     DEVICE_LOG(WARNING) << address_.ToString()
519                         << ": No update to play position";
520     return;
521   }
522 
523   auto response =
524       RegisterNotificationResponseBuilder::MakePlaybackPositionBuilder(
525           interim, status.position);
526   send_message_cb_.Run(label, false, std::move(response));
527 
528   last_play_status_.position = status.position;
529 
530   if (!interim) {
531     active_labels_.erase(label);
532     play_pos_changed_ = Notification(false, 0);
533   }
534 
535   // We still try to send updates while music is playing to the non active
536   // device even though the device thinks the music is paused. This makes
537   // the status bar on the remote device move.
538   if (status.state == PlayState::PLAYING && !IsInSilenceMode()) {
539     DEVICE_VLOG(0) << __func__ << ": Queue next play position update";
540     play_pos_update_cb_.Reset(base::Bind(&Device::HandlePlayPosUpdate,
541                                          weak_ptr_factory_.GetWeakPtr()));
542     btbase::AbstractMessageLoop::current_task_runner()->PostDelayedTask(
543         FROM_HERE, play_pos_update_cb_.callback(),
544         base::TimeDelta::FromSeconds(play_pos_interval_));
545   }
546 }
547 
548 // TODO (apanicke): Finish implementing when we add support for more than one
549 // player
AddressedPlayerNotificationResponse(uint8_t label,bool interim,uint16_t curr_player,std::vector<MediaPlayerInfo>)550 void Device::AddressedPlayerNotificationResponse(
551     uint8_t label, bool interim, uint16_t curr_player,
552     std::vector<MediaPlayerInfo> /* unused */) {
553   DEVICE_VLOG(1) << __func__
554                  << ": curr_player_id=" << (unsigned int)curr_player;
555 
556   if (interim) {
557     addr_player_changed_ = Notification(true, label);
558   } else if (!addr_player_changed_.first) {
559     DEVICE_VLOG(3) << __func__ << ": Device not registered for update";
560     return;
561   }
562 
563   // If there is no set browsed player, use the current addressed player as the
564   // default NOTE: Using any browsing commands before the browsed player is set
565   // is a violation of the AVRCP Spec but there are some carkits that try too
566   // anyways
567   if (curr_browsed_player_id_ == -1) curr_browsed_player_id_ = curr_player;
568 
569   auto response =
570       RegisterNotificationResponseBuilder::MakeAddressedPlayerBuilder(
571           interim, curr_player, 0x0000);
572   send_message_cb_.Run(label, false, std::move(response));
573 
574   if (!interim) {
575     active_labels_.erase(label);
576     addr_player_changed_ = Notification(false, 0);
577     RejectNotification();
578   }
579 }
580 
RejectNotification()581 void Device::RejectNotification() {
582   DEVICE_VLOG(1) << __func__;
583   Notification* rejectNotification[] = {&play_status_changed_, &track_changed_,
584                                         &play_pos_changed_,
585                                         &now_playing_changed_};
586   for (int i = 0; i < 4; i++) {
587     uint8_t label = rejectNotification[i]->second;
588     auto response = RejectBuilder::MakeBuilder(
589         CommandPdu::REGISTER_NOTIFICATION, Status::ADDRESSED_PLAYER_CHANGED);
590     send_message_cb_.Run(label, false, std::move(response));
591     active_labels_.erase(label);
592     rejectNotification[i] = new Notification(false, 0);
593   }
594 }
595 
GetPlayStatusResponse(uint8_t label,PlayStatus status)596 void Device::GetPlayStatusResponse(uint8_t label, PlayStatus status) {
597   DEVICE_VLOG(2) << __func__ << ": position=" << status.position
598                  << " duration=" << status.duration
599                  << " state=" << status.state;
600   auto response = GetPlayStatusResponseBuilder::MakeBuilder(
601       status.duration, status.position,
602       IsActive() ? status.state : PlayState::PAUSED);
603   send_message(label, false, std::move(response));
604 }
605 
GetElementAttributesResponse(uint8_t label,std::shared_ptr<GetElementAttributesRequest> pkt,SongInfo info)606 void Device::GetElementAttributesResponse(
607     uint8_t label, std::shared_ptr<GetElementAttributesRequest> pkt,
608     SongInfo info) {
609   auto get_element_attributes_pkt = pkt;
610   auto attributes_requested =
611       get_element_attributes_pkt->GetAttributesRequested();
612 
613   auto response = GetElementAttributesResponseBuilder::MakeBuilder(ctrl_mtu_);
614 
615   // Filter out DEFAULT_COVER_ART handle if this device has no client
616   if (!HasBipClient()) {
617     filter_cover_art(info);
618   }
619 
620   last_song_info_ = info;
621 
622   if (attributes_requested.size() != 0) {
623     for (const auto& attribute : attributes_requested) {
624       if (info.attributes.find(attribute) != info.attributes.end()) {
625         response->AddAttributeEntry(*info.attributes.find(attribute));
626       }
627     }
628   } else {  // zero attributes requested which means all attributes requested
629     for (const auto& attribute : info.attributes) {
630       response->AddAttributeEntry(attribute);
631     }
632   }
633 
634   send_message(label, false, std::move(response));
635 }
636 
MessageReceived(uint8_t label,std::shared_ptr<Packet> pkt)637 void Device::MessageReceived(uint8_t label, std::shared_ptr<Packet> pkt) {
638   if (!pkt->IsValid()) {
639     DEVICE_LOG(WARNING) << __func__ << ": Request packet is not valid";
640     auto response = RejectBuilder::MakeBuilder(static_cast<CommandPdu>(0), Status::INVALID_COMMAND);
641     send_message(label, false, std::move(response));
642     return;
643   }
644 
645   DEVICE_VLOG(4) << __func__ << ": opcode=" << pkt->GetOpcode();
646   active_labels_.insert(label);
647   switch (pkt->GetOpcode()) {
648     // TODO (apanicke): Remove handling of UNIT_INFO and SUBUNIT_INFO from
649     // the AVRC_API and instead handle it here to reduce fragmentation.
650     case Opcode::UNIT_INFO: {
651     } break;
652     case Opcode::SUBUNIT_INFO: {
653     } break;
654     case Opcode::PASS_THROUGH: {
655       auto pass_through_packet = Packet::Specialize<PassThroughPacket>(pkt);
656 
657       if (!pass_through_packet->IsValid()) {
658         DEVICE_LOG(WARNING) << __func__ << ": Request packet is not valid";
659         auto response = RejectBuilder::MakeBuilder(static_cast<CommandPdu>(0), Status::INVALID_COMMAND);
660         send_message(label, false, std::move(response));
661         return;
662       }
663 
664       auto response = PassThroughPacketBuilder::MakeBuilder(
665           true, pass_through_packet->GetKeyState() == KeyState::PUSHED,
666           pass_through_packet->GetOperationId());
667       send_message(label, false, std::move(response));
668 
669       // TODO (apanicke): Use an enum for media key ID's
670       if (pass_through_packet->GetOperationId() == 0x44 &&
671           pass_through_packet->GetKeyState() == KeyState::PUSHED) {
672         // We need to get the play status since we need to know
673         // what the actual playstate is without being modified
674         // by whether the device is active.
675         media_interface_->GetPlayStatus(base::Bind(
676             [](base::WeakPtr<Device> d, PlayStatus s) {
677               if (!d) return;
678 
679               if (!d->IsActive()) {
680                 LOG(INFO) << "Setting " << d->address_.ToString()
681                           << " to be the active device";
682                 d->media_interface_->SetActiveDevice(d->address_);
683 
684                 if (s.state == PlayState::PLAYING) {
685                   LOG(INFO)
686                       << "Skipping sendKeyEvent since music is already playing";
687                   return;
688                 }
689               }
690 
691               d->media_interface_->SendKeyEvent(0x44, KeyState::PUSHED);
692             },
693             weak_ptr_factory_.GetWeakPtr()));
694         return;
695       }
696 
697       if (IsActive()) {
698         media_interface_->SendKeyEvent(pass_through_packet->GetOperationId(),
699                                        pass_through_packet->GetKeyState());
700       }
701     } break;
702     case Opcode::VENDOR: {
703       auto vendor_pkt = Packet::Specialize<VendorPacket>(pkt);
704       VendorPacketHandler(label, vendor_pkt);
705     } break;
706   }
707 }
708 
HandlePlayItem(uint8_t label,std::shared_ptr<PlayItemRequest> pkt)709 void Device::HandlePlayItem(uint8_t label,
710                             std::shared_ptr<PlayItemRequest> pkt) {
711   DEVICE_VLOG(2) << __func__ << ": scope=" << pkt->GetScope()
712                  << " uid=" << pkt->GetUid();
713 
714   if (!pkt->IsValid()) {
715     DEVICE_LOG(WARNING) << __func__ << ": Request packet is not valid";
716     auto response = RejectBuilder::MakeBuilder(pkt->GetCommandPdu(), Status::INVALID_PARAMETER);
717     send_message(label, false, std::move(response));
718     return;
719   }
720 
721   std::string media_id = "";
722   switch (pkt->GetScope()) {
723     case Scope::NOW_PLAYING:
724       media_id = now_playing_ids_.get_media_id(pkt->GetUid());
725       break;
726     case Scope::VFS:
727       media_id = vfs_ids_.get_media_id(pkt->GetUid());
728       break;
729     default:
730       DEVICE_LOG(WARNING) << __func__ << ": Unknown scope for play item";
731   }
732 
733   if (media_id == "") {
734     DEVICE_VLOG(2) << "Could not find item";
735     auto response = RejectBuilder::MakeBuilder(CommandPdu::PLAY_ITEM,
736                                                Status::DOES_NOT_EXIST);
737     send_message(label, false, std::move(response));
738     return;
739   }
740 
741   media_interface_->PlayItem(curr_browsed_player_id_,
742                              pkt->GetScope() == Scope::NOW_PLAYING, media_id);
743 
744   auto response = PlayItemResponseBuilder::MakeBuilder(Status::NO_ERROR);
745   send_message(label, false, std::move(response));
746 }
747 
HandleSetAddressedPlayer(uint8_t label,std::shared_ptr<SetAddressedPlayerRequest> pkt,uint16_t curr_player,std::vector<MediaPlayerInfo> players)748 void Device::HandleSetAddressedPlayer(
749     uint8_t label, std::shared_ptr<SetAddressedPlayerRequest> pkt,
750     uint16_t curr_player, std::vector<MediaPlayerInfo> players) {
751   DEVICE_VLOG(2) << __func__ << ": PlayerId=" << pkt->GetPlayerId();
752 
753   if (curr_player != pkt->GetPlayerId()) {
754     DEVICE_VLOG(2) << "Reject invalid addressed player ID";
755     auto response = RejectBuilder::MakeBuilder(CommandPdu::SET_ADDRESSED_PLAYER,
756                                                Status::INVALID_PLAYER_ID);
757     send_message(label, false, std::move(response));
758     return;
759   }
760 
761   auto response =
762       SetAddressedPlayerResponseBuilder::MakeBuilder(Status::NO_ERROR);
763   send_message(label, false, std::move(response));
764 }
765 
BrowseMessageReceived(uint8_t label,std::shared_ptr<BrowsePacket> pkt)766 void Device::BrowseMessageReceived(uint8_t label,
767                                    std::shared_ptr<BrowsePacket> pkt) {
768   if (!pkt->IsValid()) {
769     DEVICE_LOG(WARNING) << __func__ << ": Request packet is not valid";
770     auto response = GeneralRejectBuilder::MakeBuilder(Status::INVALID_COMMAND);
771     send_message(label, false, std::move(response));
772     return;
773   }
774 
775   DEVICE_VLOG(1) << __func__ << ": pdu=" << pkt->GetPdu();
776 
777   switch (pkt->GetPdu()) {
778     case BrowsePdu::SET_BROWSED_PLAYER:
779       HandleSetBrowsedPlayer(label,
780                              Packet::Specialize<SetBrowsedPlayerRequest>(pkt));
781       break;
782     case BrowsePdu::GET_FOLDER_ITEMS:
783       HandleGetFolderItems(label,
784                            Packet::Specialize<GetFolderItemsRequest>(pkt));
785       break;
786     case BrowsePdu::CHANGE_PATH:
787       HandleChangePath(label, Packet::Specialize<ChangePathRequest>(pkt));
788       break;
789     case BrowsePdu::GET_ITEM_ATTRIBUTES:
790       HandleGetItemAttributes(
791           label, Packet::Specialize<GetItemAttributesRequest>(pkt));
792       break;
793     case BrowsePdu::GET_TOTAL_NUMBER_OF_ITEMS:
794       HandleGetTotalNumberOfItems(
795           label, Packet::Specialize<GetTotalNumberOfItemsRequest>(pkt));
796       break;
797     default:
798       DEVICE_LOG(WARNING) << __func__ << ": " << pkt->GetPdu();
799       auto response = GeneralRejectBuilder::MakeBuilder(Status::INVALID_COMMAND);
800       send_message(label, true, std::move(response));
801 
802       break;
803   }
804 }
805 
HandleGetFolderItems(uint8_t label,std::shared_ptr<GetFolderItemsRequest> pkt)806 void Device::HandleGetFolderItems(uint8_t label,
807                                   std::shared_ptr<GetFolderItemsRequest> pkt) {
808   if (!pkt->IsValid()) {
809     // The specific get folder items builder is unimportant on failure.
810     DEVICE_LOG(WARNING) << __func__ << ": Get folder items request packet is not valid";
811     auto response =
812         GetFolderItemsResponseBuilder::MakePlayerListBuilder(Status::INVALID_PARAMETER, 0x0000, browse_mtu_);
813     send_message(label, true, std::move(response));
814     return;
815   }
816 
817   DEVICE_VLOG(2) << __func__ << ": scope=" << pkt->GetScope();
818 
819   switch (pkt->GetScope()) {
820     case Scope::MEDIA_PLAYER_LIST:
821       media_interface_->GetMediaPlayerList(
822           base::Bind(&Device::GetMediaPlayerListResponse,
823                      weak_ptr_factory_.GetWeakPtr(), label, pkt));
824       break;
825     case Scope::VFS:
826       media_interface_->GetFolderItems(
827           curr_browsed_player_id_, CurrentFolder(),
828           base::Bind(&Device::GetVFSListResponse,
829                      weak_ptr_factory_.GetWeakPtr(), label, pkt));
830       break;
831     case Scope::NOW_PLAYING:
832       media_interface_->GetNowPlayingList(
833           base::Bind(&Device::GetNowPlayingListResponse,
834                      weak_ptr_factory_.GetWeakPtr(), label, pkt));
835       break;
836     default:
837       DEVICE_LOG(ERROR) << __func__ << ": " << pkt->GetScope();
838       auto response = GetFolderItemsResponseBuilder::MakePlayerListBuilder(Status::INVALID_PARAMETER, 0, browse_mtu_);
839       send_message(label, true, std::move(response));
840       break;
841   }
842 }
843 
HandleGetTotalNumberOfItems(uint8_t label,std::shared_ptr<GetTotalNumberOfItemsRequest> pkt)844 void Device::HandleGetTotalNumberOfItems(
845     uint8_t label, std::shared_ptr<GetTotalNumberOfItemsRequest> pkt) {
846   if (!pkt->IsValid()) {
847     DEVICE_LOG(WARNING) << __func__ << ": Request packet is not valid";
848     auto response = GetTotalNumberOfItemsResponseBuilder::MakeBuilder(Status::INVALID_PARAMETER, 0x0000, 0);
849     send_message(label, true, std::move(response));
850     return;
851   }
852 
853   DEVICE_VLOG(2) << __func__ << ": scope=" << pkt->GetScope();
854 
855   switch (pkt->GetScope()) {
856     case Scope::MEDIA_PLAYER_LIST: {
857       media_interface_->GetMediaPlayerList(
858           base::Bind(&Device::GetTotalNumberOfItemsMediaPlayersResponse,
859                      weak_ptr_factory_.GetWeakPtr(), label));
860       break;
861     }
862     case Scope::VFS:
863       media_interface_->GetFolderItems(
864           curr_browsed_player_id_, CurrentFolder(),
865           base::Bind(&Device::GetTotalNumberOfItemsVFSResponse,
866                      weak_ptr_factory_.GetWeakPtr(), label));
867       break;
868     case Scope::NOW_PLAYING:
869       media_interface_->GetNowPlayingList(
870           base::Bind(&Device::GetTotalNumberOfItemsNowPlayingResponse,
871                      weak_ptr_factory_.GetWeakPtr(), label));
872       break;
873     default:
874       DEVICE_LOG(ERROR) << __func__ << ": " << pkt->GetScope();
875       break;
876   }
877 }
878 
GetTotalNumberOfItemsMediaPlayersResponse(uint8_t label,uint16_t curr_player,std::vector<MediaPlayerInfo> list)879 void Device::GetTotalNumberOfItemsMediaPlayersResponse(
880     uint8_t label, uint16_t curr_player, std::vector<MediaPlayerInfo> list) {
881   DEVICE_VLOG(2) << __func__ << ": num_items=" << list.size();
882 
883   auto builder = GetTotalNumberOfItemsResponseBuilder::MakeBuilder(
884       Status::NO_ERROR, 0x0000, list.size());
885   send_message(label, true, std::move(builder));
886 }
887 
GetTotalNumberOfItemsVFSResponse(uint8_t label,std::vector<ListItem> list)888 void Device::GetTotalNumberOfItemsVFSResponse(uint8_t label,
889                                               std::vector<ListItem> list) {
890   DEVICE_VLOG(2) << __func__ << ": num_items=" << list.size();
891 
892   auto builder = GetTotalNumberOfItemsResponseBuilder::MakeBuilder(
893       Status::NO_ERROR, 0x0000, list.size());
894   send_message(label, true, std::move(builder));
895 }
896 
GetTotalNumberOfItemsNowPlayingResponse(uint8_t label,std::string curr_song_id,std::vector<SongInfo> list)897 void Device::GetTotalNumberOfItemsNowPlayingResponse(
898     uint8_t label, std::string curr_song_id, std::vector<SongInfo> list) {
899   DEVICE_VLOG(2) << __func__ << ": num_items=" << list.size();
900 
901   auto builder = GetTotalNumberOfItemsResponseBuilder::MakeBuilder(
902       Status::NO_ERROR, 0x0000, list.size());
903   send_message(label, true, std::move(builder));
904 }
905 
HandleChangePath(uint8_t label,std::shared_ptr<ChangePathRequest> pkt)906 void Device::HandleChangePath(uint8_t label,
907                               std::shared_ptr<ChangePathRequest> pkt) {
908   if (!pkt->IsValid()) {
909     DEVICE_LOG(WARNING) << __func__ << ": Request packet is not valid";
910     auto response = ChangePathResponseBuilder::MakeBuilder(Status::INVALID_PARAMETER, 0);
911     send_message(label, true, std::move(response));
912     return;
913   }
914 
915   DEVICE_VLOG(2) << __func__ << ": direction=" << pkt->GetDirection()
916                  << " uid=" << loghex(pkt->GetUid());
917 
918   if (pkt->GetDirection() == Direction::DOWN &&
919       vfs_ids_.get_media_id(pkt->GetUid()) == "") {
920     DEVICE_LOG(ERROR) << __func__
921                       << ": No item found for UID=" << pkt->GetUid();
922     auto builder =
923         ChangePathResponseBuilder::MakeBuilder(Status::DOES_NOT_EXIST, 0);
924     send_message(label, true, std::move(builder));
925     return;
926   }
927 
928   if (pkt->GetDirection() == Direction::DOWN) {
929     current_path_.push(vfs_ids_.get_media_id(pkt->GetUid()));
930     DEVICE_VLOG(2) << "Pushing Path to stack: \"" << CurrentFolder() << "\"";
931   } else {
932     // Don't pop the root id off the stack
933     if (current_path_.size() > 1) {
934       current_path_.pop();
935     } else {
936       DEVICE_LOG(ERROR) << "Trying to change directory up past root.";
937       auto builder =
938           ChangePathResponseBuilder::MakeBuilder(Status::DOES_NOT_EXIST, 0);
939       send_message(label, true, std::move(builder));
940       return;
941     }
942 
943     DEVICE_VLOG(2) << "Popping Path from stack: new path=\"" << CurrentFolder()
944                    << "\"";
945   }
946 
947   media_interface_->GetFolderItems(
948       curr_browsed_player_id_, CurrentFolder(),
949       base::Bind(&Device::ChangePathResponse, weak_ptr_factory_.GetWeakPtr(),
950                  label, pkt));
951 }
952 
ChangePathResponse(uint8_t label,std::shared_ptr<ChangePathRequest> pkt,std::vector<ListItem> list)953 void Device::ChangePathResponse(uint8_t label,
954                                 std::shared_ptr<ChangePathRequest> pkt,
955                                 std::vector<ListItem> list) {
956   // TODO (apanicke): Reconstruct the VFS ID's here. Right now it gets
957   // reconstructed in GetFolderItemsVFS
958   auto builder =
959       ChangePathResponseBuilder::MakeBuilder(Status::NO_ERROR, list.size());
960   send_message(label, true, std::move(builder));
961 }
962 
HandleGetItemAttributes(uint8_t label,std::shared_ptr<GetItemAttributesRequest> pkt)963 void Device::HandleGetItemAttributes(
964     uint8_t label, std::shared_ptr<GetItemAttributesRequest> pkt) {
965   if (!pkt->IsValid()) {
966     DEVICE_LOG(WARNING) << __func__ << ": Request packet is not valid";
967     auto builder = GetItemAttributesResponseBuilder::MakeBuilder(Status::INVALID_PARAMETER, browse_mtu_);
968     send_message(label, true, std::move(builder));
969     return;
970   }
971 
972   DEVICE_VLOG(2) << __func__ << ": scope=" << pkt->GetScope()
973                  << " uid=" << loghex(pkt->GetUid())
974                  << " uid counter=" << loghex(pkt->GetUidCounter());
975   if (pkt->GetUidCounter() != 0x0000) {  // For database unaware player, use 0
976     DEVICE_LOG(WARNING) << "UidCounter is invalid";
977     auto builder = GetItemAttributesResponseBuilder::MakeBuilder(
978         Status::UIDS_CHANGED, browse_mtu_);
979     send_message(label, true, std::move(builder));
980     return;
981   }
982 
983   switch (pkt->GetScope()) {
984     case Scope::NOW_PLAYING: {
985       media_interface_->GetNowPlayingList(
986           base::Bind(&Device::GetItemAttributesNowPlayingResponse,
987                      weak_ptr_factory_.GetWeakPtr(), label, pkt));
988     } break;
989     case Scope::VFS:
990       // TODO (apanicke): Check the vfs_ids_ here. If the item doesn't exist
991       // then we can auto send the error without calling up. We do this check
992       // later right now though in order to prevent race conditions with updates
993       // on the media layer.
994       media_interface_->GetFolderItems(
995           curr_browsed_player_id_, CurrentFolder(),
996           base::Bind(&Device::GetItemAttributesVFSResponse,
997                      weak_ptr_factory_.GetWeakPtr(), label, pkt));
998       break;
999     default:
1000       DEVICE_LOG(ERROR) << "UNKNOWN SCOPE FOR HANDLE GET ITEM ATTRIBUTES";
1001       break;
1002   }
1003 }
1004 
GetItemAttributesNowPlayingResponse(uint8_t label,std::shared_ptr<GetItemAttributesRequest> pkt,std::string curr_media_id,std::vector<SongInfo> song_list)1005 void Device::GetItemAttributesNowPlayingResponse(
1006     uint8_t label, std::shared_ptr<GetItemAttributesRequest> pkt,
1007     std::string curr_media_id, std::vector<SongInfo> song_list) {
1008   DEVICE_VLOG(2) << __func__ << ": uid=" << loghex(pkt->GetUid());
1009   auto builder = GetItemAttributesResponseBuilder::MakeBuilder(Status::NO_ERROR,
1010                                                                browse_mtu_);
1011 
1012   auto media_id = now_playing_ids_.get_media_id(pkt->GetUid());
1013   if (media_id == "") {
1014     media_id = curr_media_id;
1015   }
1016 
1017   DEVICE_VLOG(2) << __func__ << ": media_id=\"" << media_id << "\"";
1018 
1019   SongInfo info;
1020   if (song_list.size() == 1) {
1021     DEVICE_VLOG(2)
1022         << __func__
1023         << " Send out the only song in the queue as now playing song.";
1024     info = song_list.front();
1025   } else {
1026     for (const auto& temp : song_list) {
1027       if (temp.media_id == media_id) {
1028         info = temp;
1029       }
1030     }
1031   }
1032 
1033   // Filter out DEFAULT_COVER_ART handle if this device has no client
1034   if (!HasBipClient()) {
1035     filter_cover_art(info);
1036   }
1037 
1038   auto attributes_requested = pkt->GetAttributesRequested();
1039   if (attributes_requested.size() != 0) {
1040     for (const auto& attribute : attributes_requested) {
1041       if (info.attributes.find(attribute) != info.attributes.end()) {
1042         builder->AddAttributeEntry(*info.attributes.find(attribute));
1043       }
1044     }
1045   } else {
1046     // If zero attributes were requested, that means all attributes were
1047     // requested
1048     for (const auto& attribute : info.attributes) {
1049       builder->AddAttributeEntry(attribute);
1050     }
1051   }
1052 
1053   send_message(label, true, std::move(builder));
1054 }
1055 
GetItemAttributesVFSResponse(uint8_t label,std::shared_ptr<GetItemAttributesRequest> pkt,std::vector<ListItem> item_list)1056 void Device::GetItemAttributesVFSResponse(
1057     uint8_t label, std::shared_ptr<GetItemAttributesRequest> pkt,
1058     std::vector<ListItem> item_list) {
1059   DEVICE_VLOG(2) << __func__ << ": uid=" << loghex(pkt->GetUid());
1060 
1061   auto media_id = vfs_ids_.get_media_id(pkt->GetUid());
1062   if (media_id == "") {
1063     LOG(WARNING) << __func__ << ": Item not found";
1064     auto builder = GetItemAttributesResponseBuilder::MakeBuilder(
1065         Status::DOES_NOT_EXIST, browse_mtu_);
1066     send_message(label, true, std::move(builder));
1067     return;
1068   }
1069 
1070   auto builder = GetItemAttributesResponseBuilder::MakeBuilder(Status::NO_ERROR,
1071                                                                browse_mtu_);
1072 
1073   ListItem item_requested;
1074   for (const auto& temp : item_list) {
1075     if ((temp.type == ListItem::FOLDER && temp.folder.media_id == media_id) ||
1076         (temp.type == ListItem::SONG && temp.song.media_id == media_id)) {
1077       item_requested = temp;
1078     }
1079   }
1080 
1081   // Filter out DEFAULT_COVER_ART handle if this device has no client
1082   if (item_requested.type == ListItem::SONG && !HasBipClient()) {
1083     filter_cover_art(item_requested.song);
1084   }
1085 
1086   // TODO (apanicke): Add a helper function or allow adding a map
1087   // of attributes to GetItemAttributesResponseBuilder
1088   auto attributes_requested = pkt->GetAttributesRequested();
1089   if (item_requested.type == ListItem::FOLDER) {
1090     if (attributes_requested.size() == 0) {
1091       builder->AddAttributeEntry(Attribute::TITLE, item_requested.folder.name);
1092     } else {
1093       for (auto& attr : attributes_requested) {
1094         if (attr == Attribute::TITLE) {
1095           builder->AddAttributeEntry(Attribute::TITLE,
1096                                      item_requested.folder.name);
1097         }
1098       }
1099     }
1100   } else {
1101     if (attributes_requested.size() != 0) {
1102       for (const auto& attribute : attributes_requested) {
1103         if (item_requested.song.attributes.find(attribute) !=
1104             item_requested.song.attributes.end()) {
1105           builder->AddAttributeEntry(
1106               *item_requested.song.attributes.find(attribute));
1107         }
1108       }
1109     } else {
1110       // If zero attributes were requested, that means all attributes were
1111       // requested
1112       for (const auto& attribute : item_requested.song.attributes) {
1113         builder->AddAttributeEntry(attribute);
1114       }
1115     }
1116   }
1117 
1118   send_message(label, true, std::move(builder));
1119 }
1120 
GetMediaPlayerListResponse(uint8_t label,std::shared_ptr<GetFolderItemsRequest> pkt,uint16_t curr_player,std::vector<MediaPlayerInfo> players)1121 void Device::GetMediaPlayerListResponse(
1122     uint8_t label, std::shared_ptr<GetFolderItemsRequest> pkt,
1123     uint16_t curr_player, std::vector<MediaPlayerInfo> players) {
1124   DEVICE_VLOG(2) << __func__;
1125 
1126   if (players.size() == 0) {
1127     auto no_items_rsp = GetFolderItemsResponseBuilder::MakePlayerListBuilder(
1128         Status::RANGE_OUT_OF_BOUNDS, 0x0000, browse_mtu_);
1129     send_message(label, true, std::move(no_items_rsp));
1130   }
1131 
1132   auto builder = GetFolderItemsResponseBuilder::MakePlayerListBuilder(
1133       Status::NO_ERROR, 0x0000, browse_mtu_);
1134 
1135   // Move the current player to the first slot due to some carkits always
1136   // connecting to the first listed player rather than using the ID
1137   // returned by Addressed Player Changed
1138   for (auto it = players.begin(); it != players.end(); it++) {
1139     if (it->id == curr_player) {
1140       DEVICE_VLOG(1) << " Adding player to first spot: " << it->name;
1141       auto temp_player = *it;
1142       players.erase(it);
1143       players.insert(players.begin(), temp_player);
1144       break;
1145     }
1146   }
1147 
1148   for (size_t i = pkt->GetStartItem();
1149        i <= pkt->GetEndItem() && i < players.size(); i++) {
1150     MediaPlayerItem item(players[i].id, players[i].name,
1151                          players[i].browsing_supported);
1152     builder->AddMediaPlayer(item);
1153   }
1154 
1155   send_message(label, true, std::move(builder));
1156 }
1157 
filter_attributes_requested(const SongInfo & song,const std::vector<Attribute> & attrs)1158 std::set<AttributeEntry> filter_attributes_requested(
1159     const SongInfo& song, const std::vector<Attribute>& attrs) {
1160   std::set<AttributeEntry> result;
1161   for (const auto& attr : attrs) {
1162     if (song.attributes.find(attr) != song.attributes.end()) {
1163       result.insert(*song.attributes.find(attr));
1164     }
1165   }
1166 
1167   return result;
1168 }
1169 
GetVFSListResponse(uint8_t label,std::shared_ptr<GetFolderItemsRequest> pkt,std::vector<ListItem> items)1170 void Device::GetVFSListResponse(uint8_t label,
1171                                 std::shared_ptr<GetFolderItemsRequest> pkt,
1172                                 std::vector<ListItem> items) {
1173   DEVICE_VLOG(2) << __func__ << ": start_item=" << pkt->GetStartItem()
1174                  << " end_item=" << pkt->GetEndItem();
1175 
1176   // The builder will automatically correct the status if there are zero items
1177   auto builder = GetFolderItemsResponseBuilder::MakeVFSBuilder(
1178       Status::NO_ERROR, 0x0000, browse_mtu_);
1179 
1180   // TODO (apanicke): Add test that checks if vfs_ids_ is the correct size after
1181   // an operation.
1182   for (const auto& item : items) {
1183     if (item.type == ListItem::FOLDER) {
1184       vfs_ids_.insert(item.folder.media_id);
1185     } else if (item.type == ListItem::SONG) {
1186       vfs_ids_.insert(item.song.media_id);
1187     }
1188   }
1189 
1190   // Add the elements retrieved in the last get folder items request and map
1191   // them to UIDs The maps will be cleared every time a directory change
1192   // happens. These items do not need to correspond with the now playing list as
1193   // the UID's only need to be unique in the context of the current scope and
1194   // the current folder
1195   for (auto i = pkt->GetStartItem(); i <= pkt->GetEndItem() && i < items.size();
1196        i++) {
1197     if (items[i].type == ListItem::FOLDER) {
1198       auto folder = items[i].folder;
1199       // right now we always use folders of mixed type
1200       FolderItem folder_item(vfs_ids_.get_uid(folder.media_id), 0x00,
1201                              folder.is_playable, folder.name);
1202       if (!builder->AddFolder(folder_item)) break;
1203     } else if (items[i].type == ListItem::SONG) {
1204       auto song = items[i].song;
1205 
1206       // Filter out DEFAULT_COVER_ART handle if this device has no client
1207       if (!HasBipClient()) {
1208         filter_cover_art(song);
1209       }
1210 
1211       auto title =
1212           song.attributes.find(Attribute::TITLE) != song.attributes.end()
1213               ? song.attributes.find(Attribute::TITLE)->value()
1214               : "No Song Info";
1215       MediaElementItem song_item(vfs_ids_.get_uid(song.media_id), title,
1216                                  std::set<AttributeEntry>());
1217 
1218       if (pkt->GetNumAttributes() == 0x00) {  // All attributes requested
1219         song_item.attributes_ = std::move(song.attributes);
1220       } else {
1221         song_item.attributes_ =
1222             filter_attributes_requested(song, pkt->GetAttributesRequested());
1223       }
1224 
1225       // If we fail to add a song, don't accidentally add one later that might
1226       // fit.
1227       if (!builder->AddSong(song_item)) break;
1228     }
1229   }
1230 
1231   send_message(label, true, std::move(builder));
1232 }
1233 
GetNowPlayingListResponse(uint8_t label,std::shared_ptr<GetFolderItemsRequest> pkt,std::string,std::vector<SongInfo> song_list)1234 void Device::GetNowPlayingListResponse(
1235     uint8_t label, std::shared_ptr<GetFolderItemsRequest> pkt,
1236     std::string /* unused curr_song_id */, std::vector<SongInfo> song_list) {
1237   DEVICE_VLOG(2) << __func__;
1238   auto builder = GetFolderItemsResponseBuilder::MakeNowPlayingBuilder(
1239       Status::NO_ERROR, 0x0000, browse_mtu_);
1240 
1241   now_playing_ids_.clear();
1242   for (const SongInfo& song : song_list) {
1243     now_playing_ids_.insert(song.media_id);
1244   }
1245 
1246   for (size_t i = pkt->GetStartItem();
1247        i <= pkt->GetEndItem() && i < song_list.size(); i++) {
1248     auto song = song_list[i];
1249 
1250     // Filter out DEFAULT_COVER_ART handle if this device has no client
1251     if (!HasBipClient()) {
1252       filter_cover_art(song);
1253     }
1254 
1255     auto title = song.attributes.find(Attribute::TITLE) != song.attributes.end()
1256                      ? song.attributes.find(Attribute::TITLE)->value()
1257                      : "No Song Info";
1258 
1259     MediaElementItem item(i + 1, title, std::set<AttributeEntry>());
1260     if (pkt->GetNumAttributes() == 0x00) {
1261       item.attributes_ = std::move(song.attributes);
1262     } else {
1263       item.attributes_ =
1264           filter_attributes_requested(song, pkt->GetAttributesRequested());
1265     }
1266 
1267     // If we fail to add a song, don't accidentally add one later that might
1268     // fit.
1269     if (!builder->AddSong(item)) break;
1270   }
1271 
1272   send_message(label, true, std::move(builder));
1273 }
1274 
HandleSetBrowsedPlayer(uint8_t label,std::shared_ptr<SetBrowsedPlayerRequest> pkt)1275 void Device::HandleSetBrowsedPlayer(
1276     uint8_t label, std::shared_ptr<SetBrowsedPlayerRequest> pkt) {
1277   if (!pkt->IsValid()) {
1278     DEVICE_LOG(WARNING) << __func__ << ": Request packet is not valid";
1279     auto response = SetBrowsedPlayerResponseBuilder::MakeBuilder(Status::INVALID_PARAMETER, 0x0000, 0, 0, "");
1280     send_message(label, true, std::move(response));
1281     return;
1282   }
1283 
1284   DEVICE_VLOG(2) << __func__ << ": player_id=" << pkt->GetPlayerId();
1285   media_interface_->SetBrowsedPlayer(
1286       pkt->GetPlayerId(),
1287       base::Bind(&Device::SetBrowsedPlayerResponse,
1288                  weak_ptr_factory_.GetWeakPtr(), label, pkt));
1289 }
1290 
SetBrowsedPlayerResponse(uint8_t label,std::shared_ptr<SetBrowsedPlayerRequest> pkt,bool success,std::string root_id,uint32_t num_items)1291 void Device::SetBrowsedPlayerResponse(
1292     uint8_t label, std::shared_ptr<SetBrowsedPlayerRequest> pkt, bool success,
1293     std::string root_id, uint32_t num_items) {
1294   DEVICE_VLOG(2) << __func__ << ": success=" << success << " root_id=\""
1295                  << root_id << "\" num_items=" << num_items;
1296 
1297   if (!success) {
1298     auto response = SetBrowsedPlayerResponseBuilder::MakeBuilder(
1299         Status::INVALID_PLAYER_ID, 0x0000, num_items, 0, "");
1300     send_message(label, true, std::move(response));
1301     return;
1302   }
1303 
1304   if (pkt->GetPlayerId() == 0 && num_items == 0) {
1305     // Response fail if no browsable player in Bluetooth Player
1306     auto response = SetBrowsedPlayerResponseBuilder::MakeBuilder(
1307         Status::PLAYER_NOT_BROWSABLE, 0x0000, num_items, 0, "");
1308     send_message(label, true, std::move(response));
1309     return;
1310   }
1311 
1312   curr_browsed_player_id_ = pkt->GetPlayerId();
1313 
1314   // Clear the path and push the new root.
1315   current_path_ = std::stack<std::string>();
1316   current_path_.push(root_id);
1317 
1318   auto response = SetBrowsedPlayerResponseBuilder::MakeBuilder(
1319       Status::NO_ERROR, 0x0000, num_items, 0, "");
1320   send_message(label, true, std::move(response));
1321 }
1322 
SendMediaUpdate(bool metadata,bool play_status,bool queue)1323 void Device::SendMediaUpdate(bool metadata, bool play_status, bool queue) {
1324   bool is_silence = IsInSilenceMode();
1325 
1326   CHECK(media_interface_);
1327   DEVICE_VLOG(4) << __func__ << ": Metadata=" << metadata
1328                  << " : play_status= " << play_status << " : queue=" << queue
1329                  << " ; is_silence=" << is_silence;
1330 
1331   if (queue) {
1332     HandleNowPlayingUpdate();
1333   }
1334 
1335   if (play_status) {
1336     HandlePlayStatusUpdate();
1337     if (!is_silence) {
1338       HandlePlayPosUpdate();
1339     }
1340   }
1341 
1342   if (metadata) HandleTrackUpdate();
1343 }
1344 
SendFolderUpdate(bool available_players,bool addressed_player,bool uids)1345 void Device::SendFolderUpdate(bool available_players, bool addressed_player,
1346                               bool uids) {
1347   CHECK(media_interface_);
1348   DEVICE_VLOG(4) << __func__;
1349 
1350   if (available_players) {
1351     HandleAvailablePlayerUpdate();
1352   }
1353 
1354   if (addressed_player) {
1355     HandleAddressedPlayerUpdate();
1356   }
1357 }
1358 
HandleTrackUpdate()1359 void Device::HandleTrackUpdate() {
1360   DEVICE_VLOG(2) << __func__;
1361   if (!track_changed_.first) {
1362     LOG(WARNING) << "Device is not registered for track changed updates";
1363     return;
1364   }
1365 
1366   media_interface_->GetNowPlayingList(
1367       base::Bind(&Device::TrackChangedNotificationResponse,
1368                  weak_ptr_factory_.GetWeakPtr(), track_changed_.second, false));
1369 }
1370 
HandlePlayStatusUpdate()1371 void Device::HandlePlayStatusUpdate() {
1372   DEVICE_VLOG(2) << __func__;
1373   if (!play_status_changed_.first) {
1374     LOG(WARNING) << "Device is not registered for play status updates";
1375     return;
1376   }
1377 
1378   media_interface_->GetPlayStatus(base::Bind(
1379       &Device::PlaybackStatusNotificationResponse,
1380       weak_ptr_factory_.GetWeakPtr(), play_status_changed_.second, false));
1381 }
1382 
HandleNowPlayingUpdate()1383 void Device::HandleNowPlayingUpdate() {
1384   DEVICE_VLOG(2) << __func__;
1385 
1386   if (!now_playing_changed_.first) {
1387     LOG(WARNING) << "Device is not registered for now playing updates";
1388     return;
1389   }
1390 
1391   media_interface_->GetNowPlayingList(base::Bind(
1392       &Device::HandleNowPlayingNotificationResponse,
1393       weak_ptr_factory_.GetWeakPtr(), now_playing_changed_.second, false));
1394 }
1395 
HandleNowPlayingNotificationResponse(uint8_t label,bool interim,std::string curr_song_id,std::vector<SongInfo> song_list)1396 void Device::HandleNowPlayingNotificationResponse(
1397     uint8_t label, bool interim, std::string curr_song_id,
1398     std::vector<SongInfo> song_list) {
1399   if (interim) {
1400     now_playing_changed_ = Notification(true, label);
1401   } else if (!now_playing_changed_.first) {
1402     LOG(WARNING) << "Device is not registered for now playing updates";
1403     return;
1404   }
1405 
1406   now_playing_ids_.clear();
1407   for (const SongInfo& song : song_list) {
1408     now_playing_ids_.insert(song.media_id);
1409   }
1410 
1411   auto response =
1412       RegisterNotificationResponseBuilder::MakeNowPlayingBuilder(interim);
1413   send_message(now_playing_changed_.second, false, std::move(response));
1414 
1415   if (!interim) {
1416     active_labels_.erase(label);
1417     now_playing_changed_ = Notification(false, 0);
1418   }
1419 }
1420 
HandlePlayPosUpdate()1421 void Device::HandlePlayPosUpdate() {
1422   DEVICE_VLOG(0) << __func__;
1423   if (!play_pos_changed_.first) {
1424     LOG(WARNING) << "Device is not registered for play position updates";
1425     return;
1426   }
1427 
1428   media_interface_->GetPlayStatus(base::Bind(
1429       &Device::PlaybackPosNotificationResponse, weak_ptr_factory_.GetWeakPtr(),
1430       play_pos_changed_.second, false));
1431 }
1432 
HandleAvailablePlayerUpdate()1433 void Device::HandleAvailablePlayerUpdate() {
1434   DEVICE_VLOG(1) << __func__;
1435 
1436   if (!avail_players_changed_.first) {
1437     LOG(WARNING) << "Device is not registered for available player updates";
1438     return;
1439   }
1440 
1441   auto response =
1442       RegisterNotificationResponseBuilder::MakeAvailablePlayersBuilder(false);
1443   send_message_cb_.Run(avail_players_changed_.second, false,
1444                        std::move(response));
1445 
1446   if (!avail_players_changed_.first) {
1447     active_labels_.erase(avail_players_changed_.second);
1448     avail_players_changed_ = Notification(false, 0);
1449   }
1450 }
1451 
HandleAddressedPlayerUpdate()1452 void Device::HandleAddressedPlayerUpdate() {
1453   DEVICE_VLOG(1) << __func__;
1454   if (!addr_player_changed_.first) {
1455     DEVICE_LOG(WARNING)
1456         << "Device is not registered for addressed player updates";
1457     return;
1458   }
1459   media_interface_->GetMediaPlayerList(base::Bind(
1460       &Device::AddressedPlayerNotificationResponse,
1461       weak_ptr_factory_.GetWeakPtr(), addr_player_changed_.second, false));
1462 }
1463 
DeviceDisconnected()1464 void Device::DeviceDisconnected() {
1465   DEVICE_LOG(INFO) << "Device was disconnected";
1466   play_pos_update_cb_.Cancel();
1467 
1468   // TODO (apanicke): Once the interfaces are set in the Device construction,
1469   // remove these conditionals.
1470   if (volume_interface_ != nullptr)
1471     volume_interface_->DeviceDisconnected(GetAddress());
1472 }
1473 
volumeToStr(int8_t volume)1474 static std::string volumeToStr(int8_t volume) {
1475   if (volume == VOL_NOT_SUPPORTED) return "Absolute Volume not supported";
1476   if (volume == VOL_REGISTRATION_FAILED)
1477     return "Volume changed notification was rejected";
1478   return std::to_string(volume);
1479 }
1480 
operator <<(std::ostream & out,const Device & d)1481 std::ostream& operator<<(std::ostream& out, const Device& d) {
1482   out << d.address_.ToString();
1483   if (d.IsActive()) out << " <Active>";
1484   out << std::endl;
1485 
1486   ScopedIndent indent(out);
1487   out << "Current Volume: " << volumeToStr(d.volume_) << std::endl;
1488   out << "Current Browsed Player ID: " << d.curr_browsed_player_id_
1489       << std::endl;
1490   out << "Registered Notifications:\n";
1491   {
1492     ScopedIndent indent(out);
1493     if (d.track_changed_.first) out << "Track Changed\n";
1494     if (d.play_status_changed_.first) out << "Play Status\n";
1495     if (d.play_pos_changed_.first) out << "Play Position\n";
1496     if (d.now_playing_changed_.first) out << "Now Playing\n";
1497     if (d.addr_player_changed_.first) out << "Addressed Player\n";
1498     if (d.avail_players_changed_.first) out << "Available Players\n";
1499     if (d.uids_changed_.first) out << "UIDs Changed\n";
1500   }
1501   out << "Last Play State: " << d.last_play_status_.state << std::endl;
1502   out << "Last Song Sent ID: \"" << d.last_song_info_.media_id << "\"\n";
1503   out << "Current Folder: \"" << d.CurrentFolder() << "\"\n";
1504   out << "MTU Sizes: CTRL=" << d.ctrl_mtu_ << " BROWSE=" << d.browse_mtu_
1505       << std::endl;
1506   // TODO (apanicke): Add supported features as well as media keys
1507   return out;
1508 }
1509 
1510 }  // namespace avrcp
1511 }  // namespace bluetooth
1512