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