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