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