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