• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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