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