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