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