| /* |
| * Copyright 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #pragma once |
| |
| #include <iostream> |
| #include <memory> |
| #include <stack> |
| |
| #include <base/bind.h> |
| #include <base/cancelable_callback.h> |
| |
| #include "avrcp.h" |
| #include "avrcp_internal.h" |
| #include "avrcp_packet.h" |
| #include "media_id_map.h" |
| #include "raw_address.h" |
| |
| namespace bluetooth { |
| namespace avrcp { |
| |
| /** |
| * A class representing a connection with a remote AVRCP device. It holds all |
| * the state and message handling for the device that it represents. |
| */ |
| // TODO (apanicke): Once we move over to having the individual message |
| // responders for Browse and Classic AVRCP Messages move the device around via a |
| // weak pointer. |
| class Device { |
| public: |
| /** |
| * Device is friends with Avrcp::ConnectionHandler so that ConnectionHandler |
| * can deliver messages to individual devices. |
| */ |
| friend class ConnectionHandler; |
| |
| 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); |
| virtual ~Device() = default; |
| |
| const RawAddress& GetAddress() const { return address_; }; |
| |
| /** |
| * Disconnects the AVRCP connection that this device represents. |
| */ |
| bool Disconnect(); |
| |
| /** |
| * Returns true if the current device is active. |
| */ |
| bool IsActive() const; |
| |
| /** |
| * Register the interfaces that the device uses to get information. If the |
| * Volume Interface is null, then absolute volume is disabled. |
| * TODO (apanicke): Add these to the constructor/factory so that each device |
| * is created valid and can't be accidentally interacted with when no |
| * interfaces are registered. |
| */ |
| void RegisterInterfaces(MediaInterface* interface, |
| A2dpInterface* a2dp_interface, |
| VolumeInterface* volume_interface); |
| |
| /** |
| * Notify the device that metadata, play_status, and/or queue have updated |
| * via a boolean. Each boolean represents whether its respective content has |
| * updated. |
| */ |
| virtual void SendMediaUpdate(bool metadata, bool play_status, bool queue); |
| |
| /** |
| * Notify the device that the available_player, addressed_player, or UIDs |
| * have updated via a boolean. Each boolean represents whether its respective |
| * content has updated. |
| */ |
| virtual void SendFolderUpdate(bool available_player, bool addressed_player, |
| bool uids); |
| |
| // TODO (apanicke): Split the message handlers into two files. One |
| // for handling Browse Messages and the other for handling all other |
| // messages. This prevents the .cc file from getting bloated like it is |
| // now. The Device class will then become a state holder for each message |
| // and all the functions in these handler classes can be static since the |
| // device will be passed in. The extensions of the Device class can contain |
| // any interop handling for specific messages on specific devices. |
| |
| void MessageReceived(uint8_t label, std::shared_ptr<Packet> pkt); |
| void BrowseMessageReceived(uint8_t label, std::shared_ptr<BrowsePacket> pkt); |
| void VendorPacketHandler(uint8_t label, std::shared_ptr<VendorPacket> pkt); |
| |
| /******************** |
| * MESSAGE RESPONSES |
| ********************/ |
| // CURRENT TRACK CHANGED |
| virtual void HandleTrackUpdate(); |
| virtual void TrackChangedNotificationResponse( |
| uint8_t label, bool interim, std::string curr_song_id, |
| std::vector<SongInfo> song_list); |
| |
| // GET CAPABILITY |
| virtual void HandleGetCapabilities( |
| uint8_t label, const std::shared_ptr<GetCapabilitiesRequest>& pkt); |
| |
| // REGISTER NOTIFICATION |
| virtual void HandleNotification( |
| uint8_t label, const std::shared_ptr<RegisterNotificationRequest>& pkt); |
| |
| // PLAY STATUS CHANGED |
| virtual void HandlePlayStatusUpdate(); |
| |
| // NOW PLAYING LIST CHANGED |
| virtual void HandleNowPlayingUpdate(); |
| virtual void HandleNowPlayingNotificationResponse( |
| uint8_t label, bool interim, std::string curr_song_id, |
| std::vector<SongInfo> song_list); |
| |
| // PLAY POSITION CHANGED |
| virtual void HandlePlayPosUpdate(); |
| virtual void PlaybackPosNotificationResponse(uint8_t label, bool interim, |
| PlayStatus status); |
| |
| // GET PLAY STATUS |
| virtual void GetPlayStatusResponse(uint8_t label, PlayStatus status); |
| virtual void PlaybackStatusNotificationResponse(uint8_t label, bool interim, |
| PlayStatus status); |
| |
| // GET ELEMENT ATTRIBUTE |
| // TODO (apanicke): Add a Handler function for this so if a specific device |
| // needs to implement an interop fix, you only need to overload the one |
| // function. |
| virtual void GetElementAttributesResponse( |
| uint8_t label, std::shared_ptr<GetElementAttributesRequest> pkt, |
| SongInfo info); |
| |
| // AVAILABLE PLAYER CHANGED |
| virtual void HandleAvailablePlayerUpdate(); |
| |
| // ADDRESSED PLAYER CHANGED |
| virtual void HandleAddressedPlayerUpdate(); |
| virtual void RejectNotification(); |
| virtual void AddressedPlayerNotificationResponse( |
| uint8_t label, bool interim, uint16_t curr_player, |
| std::vector<MediaPlayerInfo> /* unused */); |
| |
| // GET FOLDER ITEMS |
| virtual void HandleGetFolderItems( |
| uint8_t label, std::shared_ptr<GetFolderItemsRequest> request); |
| virtual void GetMediaPlayerListResponse( |
| uint8_t label, std::shared_ptr<GetFolderItemsRequest> pkt, |
| uint16_t curr_player, std::vector<MediaPlayerInfo> players); |
| virtual void GetVFSListResponse(uint8_t label, |
| std::shared_ptr<GetFolderItemsRequest> pkt, |
| std::vector<ListItem> items); |
| virtual void GetNowPlayingListResponse( |
| uint8_t label, std::shared_ptr<GetFolderItemsRequest> pkt, |
| std::string curr_song_id, std::vector<SongInfo> song_list); |
| |
| // GET TOTAL NUMBER OF ITEMS |
| virtual void HandleGetTotalNumberOfItems( |
| uint8_t label, std::shared_ptr<GetTotalNumberOfItemsRequest> pkt); |
| virtual void GetTotalNumberOfItemsMediaPlayersResponse( |
| uint8_t label, uint16_t curr_player, std::vector<MediaPlayerInfo> list); |
| virtual void GetTotalNumberOfItemsVFSResponse(uint8_t label, |
| std::vector<ListItem> items); |
| virtual void GetTotalNumberOfItemsNowPlayingResponse( |
| uint8_t label, std::string curr_song_id, std::vector<SongInfo> song_list); |
| |
| // GET ITEM ATTRIBUTES |
| virtual void HandleGetItemAttributes( |
| uint8_t label, std::shared_ptr<GetItemAttributesRequest> request); |
| virtual void GetItemAttributesNowPlayingResponse( |
| uint8_t label, std::shared_ptr<GetItemAttributesRequest> pkt, |
| std::string curr_media_id, std::vector<SongInfo> song_list); |
| virtual void GetItemAttributesVFSResponse( |
| uint8_t label, std::shared_ptr<GetItemAttributesRequest> pkt, |
| std::vector<ListItem> item_list); |
| |
| // SET BROWSED PLAYER |
| virtual void HandleSetBrowsedPlayer( |
| uint8_t label, std::shared_ptr<SetBrowsedPlayerRequest> request); |
| virtual void SetBrowsedPlayerResponse( |
| uint8_t label, std::shared_ptr<SetBrowsedPlayerRequest> pkt, bool success, |
| std::string root_id, uint32_t num_items); |
| |
| // CHANGE PATH |
| virtual void HandleChangePath(uint8_t label, |
| std::shared_ptr<ChangePathRequest> request); |
| virtual void ChangePathResponse(uint8_t label, |
| std::shared_ptr<ChangePathRequest> request, |
| std::vector<ListItem> list); |
| |
| // PLAY ITEM |
| virtual void HandlePlayItem(uint8_t label, |
| std::shared_ptr<PlayItemRequest> request); |
| |
| // SET ADDRESSED PLAYER |
| virtual void HandleSetAddressedPlayer( |
| uint8_t label, std::shared_ptr<SetAddressedPlayerRequest> request, |
| uint16_t curr_player, std::vector<MediaPlayerInfo> players); |
| |
| /******************** |
| * MESSAGE REQUESTS |
| ********************/ |
| // VOLUME CHANGED NOTIFICATION |
| virtual void RegisterVolumeChanged(); |
| virtual void HandleVolumeChanged( |
| uint8_t label, const std::shared_ptr<RegisterNotificationResponse>& pkt); |
| |
| // SET VOLUME |
| virtual void SetVolume(int8_t volume); |
| |
| /** |
| * This function is called by Avrcp::ConnectionHandler to signify that |
| * the remote device was disconnected. |
| * |
| * TODO (apanicke): Prevent allowing responses to messages while the device is |
| * disconnected by using a weak pointer handle to the device when we separate |
| * out the message handling. Also separate the logic in the future when |
| * disconnecting only browsing (Though this shouldn't matter as if we are |
| * disconnecting browsing then we should be fully disconnecting the device). |
| */ |
| void DeviceDisconnected(); |
| |
| friend std::ostream& operator<<(std::ostream& out, const Device& c); |
| |
| private: |
| // This should always contain one item which represents the root id on the |
| // current player. |
| std::string CurrentFolder() const { |
| if (current_path_.empty()) return ""; |
| return current_path_.top(); |
| } |
| |
| void send_message(uint8_t label, bool browse, |
| std::unique_ptr<::bluetooth::PacketBuilder> message) { |
| active_labels_.erase(label); |
| send_message_cb_.Run(label, browse, std::move(message)); |
| } |
| base::WeakPtrFactory<Device> weak_ptr_factory_; |
| |
| // TODO (apanicke): Initialize all the variables in the constructor. |
| RawAddress address_; |
| |
| // Enables AVRCP 1.3 Compatibility mode. This disables any AVRCP 1.4+ features |
| // such as browsing and playlists but has the highest chance of working. |
| bool avrcp13_compatibility_ = false; |
| base::Callback<void(uint8_t label, bool browse, |
| std::unique_ptr<::bluetooth::PacketBuilder> message)> |
| send_message_cb_; |
| uint16_t ctrl_mtu_; |
| uint16_t browse_mtu_; |
| |
| int curr_browsed_player_id_ = -1; |
| |
| std::stack<std::string> current_path_; |
| |
| // Notification Trackers |
| using Notification = std::pair<bool, uint8_t>; |
| Notification track_changed_ = Notification(false, 0); |
| Notification play_status_changed_ = Notification(false, 0); |
| Notification play_pos_changed_ = Notification(false, 0); |
| Notification now_playing_changed_ = Notification(false, 0); |
| Notification addr_player_changed_ = Notification(false, 0); |
| Notification avail_players_changed_ = Notification(false, 0); |
| Notification uids_changed_ = Notification(false, 0); |
| |
| MediaIdMap vfs_ids_; |
| MediaIdMap now_playing_ids_; |
| |
| uint32_t play_pos_interval_ = 0; |
| |
| SongInfo last_song_info_; |
| PlayStatus last_play_status_; |
| |
| base::CancelableClosure play_pos_update_cb_; |
| |
| MediaInterface* media_interface_ = nullptr; |
| A2dpInterface* a2dp_interface_ = nullptr; |
| VolumeInterface* volume_interface_ = nullptr; |
| |
| // Labels used for messages currently in flight. |
| std::set<uint8_t> active_labels_; |
| |
| int8_t volume_ = -1; |
| DISALLOW_COPY_AND_ASSIGN(Device); |
| }; |
| |
| } // namespace avrcp |
| } // namespace bluetooth |