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