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