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