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