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