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