| /****************************************************************************** |
| * |
| * Copyright 2009-2016 Broadcom Corporation |
| * |
| * 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. |
| * |
| ******************************************************************************/ |
| |
| #define LOG_TAG "btif_av" |
| |
| #include "btif_av.h" |
| |
| #include <base/bind.h> |
| #include <base/logging.h> |
| #include <base/strings/stringprintf.h> |
| #include <string.h> |
| #include <map> |
| |
| #include <hardware/bluetooth.h> |
| #include <hardware/bt_av.h> |
| #include <hardware/bt_rc.h> |
| |
| #include "audio_a2dp_hw/include/audio_a2dp_hw.h" |
| #include "bt_common.h" |
| #include "bt_utils.h" |
| #include "bta_api.h" |
| #include "btif_a2dp.h" |
| #include "btif_a2dp_control.h" |
| #include "btif_a2dp_sink.h" |
| #include "btif_av_co.h" |
| #include "btif_profile_queue.h" |
| #include "btif_rc.h" |
| #include "btif_state_machine.h" |
| #include "btif_util.h" |
| #include "btu.h" |
| #include "osi/include/allocator.h" |
| #include "osi/include/osi.h" |
| #include "osi/include/properties.h" |
| |
| /***************************************************************************** |
| * Constants & Macros |
| *****************************************************************************/ |
| static const std::string kBtifAvSourceServiceName = "Advanced Audio Source"; |
| static const std::string kBtifAvSinkServiceName = "Advanced Audio Sink"; |
| static const std::string kBtifAvMaxConnectedAudioDevices = |
| "persist.bluetooth.maxconnectedaudiodevices"; |
| static constexpr int kDefaultMaxConnectedAudioDevices = 1; |
| static constexpr tBTA_AV_HNDL kBtaHandleUnknown = 0; |
| |
| /***************************************************************************** |
| * Local type definitions |
| *****************************************************************************/ |
| |
| typedef struct { |
| int sample_rate; |
| int channel_count; |
| RawAddress peer_bd; |
| } btif_av_sink_config_req_t; |
| |
| /** |
| * BTIF AV events |
| */ |
| typedef enum { |
| /* Reuse BTA_AV_XXX_EVT - No need to redefine them here */ |
| BTIF_AV_CONNECT_REQ_EVT = BTA_AV_MAX_EVT, |
| BTIF_AV_DISCONNECT_REQ_EVT, |
| BTIF_AV_START_STREAM_REQ_EVT, |
| BTIF_AV_STOP_STREAM_REQ_EVT, |
| BTIF_AV_SUSPEND_STREAM_REQ_EVT, |
| BTIF_AV_SINK_CONFIG_REQ_EVT, |
| BTIF_AV_ACL_DISCONNECTED, |
| BTIF_AV_OFFLOAD_START_REQ_EVT, |
| BTIF_AV_AVRCP_OPEN_EVT, |
| BTIF_AV_AVRCP_CLOSE_EVT, |
| BTIF_AV_AVRCP_REMOTE_PLAY_EVT, |
| } btif_av_sm_event_t; |
| |
| class BtifAvEvent { |
| public: |
| BtifAvEvent(uint32_t event, const void* p_data, size_t data_length); |
| BtifAvEvent(const BtifAvEvent& other); |
| BtifAvEvent() = delete; |
| ~BtifAvEvent(); |
| BtifAvEvent& operator=(const BtifAvEvent& other); |
| |
| uint32_t Event() const { return event_; } |
| void* Data() const { return data_; } |
| size_t DataLength() const { return data_length_; } |
| std::string ToString() const; |
| static std::string EventName(uint32_t event); |
| |
| private: |
| void DeepCopy(uint32_t event, const void* p_data, size_t data_length); |
| void DeepFree(); |
| |
| uint32_t event_; |
| void* data_; |
| size_t data_length_; |
| }; |
| |
| class BtifAvPeer; |
| |
| // Should not need dedicated Suspend state as actual actions are no |
| // different than Open state. Suspend flags are needed however to prevent |
| // media task from trying to restart stream during remote Suspend or while |
| // we are in the process of a local Suspend. |
| class BtifAvStateMachine : public BtifStateMachine { |
| public: |
| enum { |
| kStateIdle, // AVDTP disconnected |
| kStateOpening, // Opening AVDTP connection |
| kStateOpened, // AVDTP is in OPEN state |
| kStateStarted, // A2DP stream started |
| kStateClosing, // Closing AVDTP connection |
| }; |
| |
| class StateIdle : public State { |
| public: |
| StateIdle(BtifAvStateMachine& sm) |
| : State(sm, kStateIdle), peer_(sm.Peer()) {} |
| void OnEnter() override; |
| void OnExit() override; |
| bool ProcessEvent(uint32_t event, void* p_data) override; |
| |
| private: |
| BtifAvPeer& peer_; |
| }; |
| |
| class StateOpening : public State { |
| public: |
| StateOpening(BtifAvStateMachine& sm) |
| : State(sm, kStateOpening), peer_(sm.Peer()) {} |
| void OnEnter() override; |
| void OnExit() override; |
| bool ProcessEvent(uint32_t event, void* p_data) override; |
| |
| private: |
| BtifAvPeer& peer_; |
| }; |
| |
| class StateOpened : public State { |
| public: |
| StateOpened(BtifAvStateMachine& sm) |
| : State(sm, kStateOpened), peer_(sm.Peer()) {} |
| void OnEnter() override; |
| void OnExit() override; |
| bool ProcessEvent(uint32_t event, void* p_data) override; |
| |
| private: |
| BtifAvPeer& peer_; |
| }; |
| |
| class StateStarted : public State { |
| public: |
| StateStarted(BtifAvStateMachine& sm) |
| : State(sm, kStateStarted), peer_(sm.Peer()) {} |
| void OnEnter() override; |
| void OnExit() override; |
| bool ProcessEvent(uint32_t event, void* p_data) override; |
| |
| private: |
| BtifAvPeer& peer_; |
| }; |
| |
| class StateClosing : public State { |
| public: |
| StateClosing(BtifAvStateMachine& sm) |
| : State(sm, kStateClosing), peer_(sm.Peer()) {} |
| void OnEnter() override; |
| void OnExit() override; |
| bool ProcessEvent(uint32_t event, void* p_data) override; |
| |
| private: |
| BtifAvPeer& peer_; |
| }; |
| |
| BtifAvStateMachine(BtifAvPeer& btif_av_peer) : peer_(btif_av_peer) { |
| state_idle_ = new StateIdle(*this); |
| state_opening_ = new StateOpening(*this); |
| state_opened_ = new StateOpened(*this); |
| state_started_ = new StateStarted(*this); |
| state_closing_ = new StateClosing(*this); |
| |
| AddState(state_idle_); |
| AddState(state_opening_); |
| AddState(state_opened_); |
| AddState(state_started_); |
| AddState(state_closing_); |
| SetInitialState(state_idle_); |
| } |
| |
| BtifAvPeer& Peer() { return peer_; } |
| |
| private: |
| BtifAvPeer& peer_; |
| StateIdle* state_idle_; |
| StateOpening* state_opening_; |
| StateOpened* state_opened_; |
| StateStarted* state_started_; |
| StateClosing* state_closing_; |
| }; |
| |
| class BtifAvPeer { |
| public: |
| enum { |
| kFlagLocalSuspendPending = 0x1, |
| kFlagRemoteSuspend = 0x2, |
| kFlagPendingStart = 0x4, |
| kFlagPendingStop = 0x8, |
| }; |
| static constexpr period_ms_t kTimeoutAvOpenOnRcMs = 2 * 1000; // 2s |
| |
| BtifAvPeer(const RawAddress& peer_address, uint8_t peer_sep, |
| tBTA_AV_HNDL bta_handle, uint8_t peer_id); |
| ~BtifAvPeer(); |
| |
| bt_status_t Init(); |
| void Cleanup(); |
| |
| /** |
| * Check whether the peer can be deleted. |
| * |
| * @return true if the pair can be deleted, otherwise false |
| */ |
| bool CanBeDeleted() const; |
| |
| const RawAddress& PeerAddress() const { return peer_address_; } |
| bool IsSource() const { return (peer_sep_ == AVDT_TSEP_SRC); } |
| bool IsSink() const { return (peer_sep_ == AVDT_TSEP_SNK); } |
| uint8_t PeerSep() const { return peer_sep_; } |
| /** |
| * Get the local device's Service Class UUID |
| * |
| * @return the local device's Service Class UUID: UUID_SERVCLASS_AUDIO_SOURCE |
| * or UUID_SERVCLASS_AUDIO_SINK |
| */ |
| uint16_t LocalUuidServiceClass() const { |
| return (IsSink() ? UUID_SERVCLASS_AUDIO_SOURCE : UUID_SERVCLASS_AUDIO_SINK); |
| } |
| tBTA_AV_HNDL BtaHandle() const { return bta_handle_; } |
| void SetBtaHandle(tBTA_AV_HNDL bta_handle) { bta_handle_ = bta_handle; } |
| uint8_t PeerId() const { return peer_id_; } |
| |
| BtifAvStateMachine& StateMachine() { return state_machine_; } |
| const BtifAvStateMachine& StateMachine() const { return state_machine_; } |
| alarm_t* AvOpenOnRcTimer() { return av_open_on_rc_timer_; } |
| const alarm_t* AvOpenOnRcTimer() const { return av_open_on_rc_timer_; } |
| |
| void SetEdr(tBTA_AV_EDR edr) { edr_ = edr; } |
| bool IsEdr() const { return (edr_ != 0); } |
| bool Is3Mbps() const { return ((edr_ & BTA_AV_EDR_3MBPS) != 0); } |
| |
| bool IsConnected() const; |
| bool IsStreaming() const; |
| |
| /** |
| * Check whether any of the flags specified by the bitlags mask is set. |
| * |
| * @param bitflags_mask the bitflags to check |
| * @return true if any of the flags to check is set, otherwise false. |
| */ |
| bool CheckFlags(uint8_t bitflags_mask) const { |
| return ((flags_ & bitflags_mask) != 0); |
| } |
| |
| /** |
| * Set only the flags as specified by the bitflags mask. |
| * |
| * @param bitflags_mask the bitflags to set |
| */ |
| void SetFlags(uint8_t bitflags_mask) { flags_ |= bitflags_mask; } |
| |
| /** |
| * Clear only the flags as specified by the bitflags mask. |
| * |
| * @param bitflags_mask the bitflags to clear |
| */ |
| void ClearFlags(uint8_t bitflags_mask) { flags_ &= ~bitflags_mask; } |
| |
| /** |
| * Clear all flags. |
| */ |
| void ClearAllFlags() { flags_ = 0; } |
| |
| /** |
| * Get a string representation of the flags that are set. |
| */ |
| std::string FlagsToString() const; |
| |
| bool SelfInitiatedConnection() const { return self_initiated_connection_; } |
| void SetSelfInitiatedConnection(bool v) { self_initiated_connection_ = v; } |
| |
| private: |
| const RawAddress peer_address_; |
| const uint8_t peer_sep_; // SEP type of peer device |
| tBTA_AV_HNDL bta_handle_; |
| const uint8_t peer_id_; |
| BtifAvStateMachine state_machine_; |
| alarm_t* av_open_on_rc_timer_; |
| tBTA_AV_EDR edr_; |
| uint8_t flags_; |
| bool self_initiated_connection_; |
| }; |
| |
| class BtifAvSource { |
| public: |
| // The PeerId is used as AppId for BTA_AvRegister() purpose |
| static constexpr uint8_t kPeerIdMin = 0; |
| static constexpr uint8_t kPeerIdMax = BTA_AV_NUM_STRS; |
| |
| BtifAvSource() |
| : callbacks_(nullptr), |
| enabled_(false), |
| max_connected_peers_(kDefaultMaxConnectedAudioDevices) {} |
| ~BtifAvSource(); |
| |
| btav_source_callbacks_t* Callbacks() { return callbacks_; } |
| bt_status_t Init( |
| btav_source_callbacks_t* callbacks, |
| const std::vector<btav_a2dp_codec_config_t>& codec_priorities); |
| void Cleanup(); |
| |
| bool Enabled() const { return enabled_; } |
| BtifAvPeer* FindPeer(const RawAddress& peer_address); |
| BtifAvPeer* FindPeerByHandle(tBTA_AV_HNDL bta_handle); |
| BtifAvPeer* FindPeerByPeerId(uint8_t peer_id); |
| BtifAvPeer* FindOrCreatePeer(const RawAddress& peer_address, |
| tBTA_AV_HNDL bta_handle); |
| |
| /** |
| * Check whether a connection to a peer is allowed. |
| * The check considers the maximum number of connected peers. |
| * |
| * @param peer_address the peer address to connect to |
| * @return true if connection is allowed, otherwise false |
| */ |
| bool AllowedToConnect(const RawAddress& peer_address) const; |
| |
| /** |
| * Delete a peer. |
| * |
| * @param peer_address the peer to delete |
| * @return true on success, otherwise false |
| */ |
| bool DeletePeer(const RawAddress& peer_address); |
| |
| /** |
| * Delete all peers that have transitioned to Idle state and can be deleted. |
| * If a peer was just created/initialized, then it cannot be deleted yet. |
| */ |
| void DeleteIdlePeers(); |
| |
| const RawAddress& ActivePeer() const { return active_peer_; } |
| bool SetActivePeer(const RawAddress& peer_address) { |
| if (active_peer_ == peer_address) return true; // Nothing has changed |
| if (bta_av_co_set_active_peer(peer_address)) { |
| active_peer_ = peer_address; |
| return true; |
| } |
| return false; |
| } |
| |
| const std::map<RawAddress, BtifAvPeer*>& Peers() const { return peers_; } |
| |
| void RegisterAllBtaHandles(); |
| void DeregisterAllBtaHandles(); |
| void BtaHandleRegistered(uint8_t peer_id, tBTA_AV_HNDL bta_handle); |
| |
| private: |
| void CleanupAllPeers(); |
| |
| btav_source_callbacks_t* callbacks_; |
| bool enabled_; |
| int max_connected_peers_; |
| std::map<RawAddress, BtifAvPeer*> peers_; |
| RawAddress active_peer_; |
| std::map<uint8_t, tBTA_AV_HNDL> peer_id2bta_handle_; |
| std::vector<btav_a2dp_codec_config_t> codec_priorities_; |
| }; |
| |
| class BtifAvSink { |
| public: |
| // The PeerId is used as AppId for BTA_AvRegister() purpose |
| static constexpr uint8_t kPeerIdMin = 0; |
| static constexpr uint8_t kPeerIdMax = BTA_AV_NUM_STRS; |
| |
| BtifAvSink() |
| : callbacks_(nullptr), |
| enabled_(false), |
| max_connected_peers_(kDefaultMaxConnectedAudioDevices) {} |
| ~BtifAvSink(); |
| bt_status_t Init(btav_sink_callbacks_t* callbacks); |
| |
| btav_sink_callbacks_t* Callbacks() { return callbacks_; } |
| void Cleanup(); |
| |
| bool Enabled() const { return enabled_; } |
| BtifAvPeer* FindPeer(const RawAddress& peer_address); |
| BtifAvPeer* FindPeerByHandle(tBTA_AV_HNDL bta_handle); |
| BtifAvPeer* FindPeerByPeerId(uint8_t peer_id); |
| BtifAvPeer* FindOrCreatePeer(const RawAddress& peer_address, |
| tBTA_AV_HNDL bta_handle); |
| |
| /** |
| * Check whether a connection to a peer is allowed. |
| * The check considers the maximum number of connected peers. |
| * |
| * @param peer_address the peer address to connect to |
| * @return true if connection is allowed, otherwise false |
| */ |
| bool AllowedToConnect(const RawAddress& peer_address) const; |
| |
| /** |
| * Delete a peer. |
| * |
| * @param peer_address the peer to delete |
| * @return true on success, otherwise false |
| */ |
| bool DeletePeer(const RawAddress& peer_address); |
| |
| /** |
| * Delete all peers that have transitioned to Idle state and can be deleted. |
| * If a peer was just created/initialized, then it cannot be deleted yet. |
| */ |
| void DeleteIdlePeers(); |
| |
| const RawAddress& ActivePeer() const { return active_peer_; } |
| bool SetActivePeer(const RawAddress& peer_address) { |
| if (bta_av_co_set_active_peer(peer_address)) { |
| active_peer_ = peer_address; |
| return true; |
| } |
| return false; |
| } |
| |
| const std::map<RawAddress, BtifAvPeer*>& Peers() const { return peers_; } |
| |
| void RegisterAllBtaHandles(); |
| void DeregisterAllBtaHandles(); |
| void BtaHandleRegistered(uint8_t peer_id, tBTA_AV_HNDL bta_handle); |
| |
| private: |
| void CleanupAllPeers(); |
| |
| btav_sink_callbacks_t* callbacks_; |
| bool enabled_; |
| int max_connected_peers_; |
| std::map<RawAddress, BtifAvPeer*> peers_; |
| RawAddress active_peer_; |
| std::map<uint8_t, tBTA_AV_HNDL> peer_id2bta_handle_; |
| }; |
| |
| /***************************************************************************** |
| * Static variables |
| *****************************************************************************/ |
| static BtifAvSource btif_av_source; |
| static BtifAvSink btif_av_sink; |
| |
| /* Helper macro to avoid code duplication in the state machine handlers */ |
| #define CHECK_RC_EVENT(e, d) \ |
| case BTA_AV_RC_OPEN_EVT: \ |
| case BTA_AV_RC_BROWSE_OPEN_EVT: \ |
| case BTA_AV_RC_CLOSE_EVT: \ |
| case BTA_AV_RC_BROWSE_CLOSE_EVT: \ |
| case BTA_AV_REMOTE_CMD_EVT: \ |
| case BTA_AV_VENDOR_CMD_EVT: \ |
| case BTA_AV_META_MSG_EVT: \ |
| case BTA_AV_RC_FEAT_EVT: \ |
| case BTA_AV_REMOTE_RSP_EVT: { \ |
| btif_rc_handler(e, d); \ |
| } break; |
| |
| static bt_status_t src_disconnect_sink(const RawAddress& peer_address); |
| static bt_status_t sink_disconnect_src(const RawAddress& peer_address); |
| static void btif_av_source_dispatch_sm_event(const RawAddress& peer_address, |
| btif_av_sm_event_t event); |
| static void btif_av_sink_dispatch_sm_event(const RawAddress& peer_address, |
| btif_av_sm_event_t event); |
| static void btif_av_handle_event(uint8_t peer_sep, |
| const RawAddress& peer_address, |
| tBTA_AV_HNDL bta_handle, |
| const BtifAvEvent& btif_av_event); |
| static void btif_report_connection_state(const RawAddress& peer_address, |
| btav_connection_state_t state); |
| static void btif_report_audio_state(const RawAddress& peer_address, |
| btav_audio_state_t state); |
| static void btif_av_source_initiate_av_open_timer_timeout(void* data); |
| static void btif_av_sink_initiate_av_open_timer_timeout(void* data); |
| static void bta_av_sink_media_callback(tBTA_AV_EVT event, |
| tBTA_AV_MEDIA* p_data); |
| |
| static BtifAvPeer* btif_av_source_find_peer(const RawAddress& peer_address) { |
| return btif_av_source.FindPeer(peer_address); |
| } |
| static BtifAvPeer* btif_av_sink_find_peer(const RawAddress& peer_address) { |
| return btif_av_sink.FindPeer(peer_address); |
| } |
| static BtifAvPeer* btif_av_find_peer(const RawAddress& peer_address) { |
| if (btif_av_source.Enabled()) return btif_av_source_find_peer(peer_address); |
| if (btif_av_sink.Enabled()) return btif_av_sink_find_peer(peer_address); |
| return nullptr; |
| } |
| static BtifAvPeer* btif_av_find_active_peer() { |
| if (btif_av_source.Enabled()) |
| return btif_av_source_find_peer(btif_av_source.ActivePeer()); |
| if (btif_av_sink.Enabled()) |
| return btif_av_sink_find_peer(btif_av_sink.ActivePeer()); |
| return nullptr; |
| } |
| |
| /***************************************************************************** |
| * Local helper functions |
| *****************************************************************************/ |
| |
| const char* dump_av_sm_event_name(btif_av_sm_event_t event) { |
| switch ((int)event) { |
| CASE_RETURN_STR(BTA_AV_ENABLE_EVT) |
| CASE_RETURN_STR(BTA_AV_REGISTER_EVT) |
| CASE_RETURN_STR(BTA_AV_OPEN_EVT) |
| CASE_RETURN_STR(BTA_AV_CLOSE_EVT) |
| CASE_RETURN_STR(BTA_AV_START_EVT) |
| CASE_RETURN_STR(BTA_AV_STOP_EVT) |
| CASE_RETURN_STR(BTA_AV_PROTECT_REQ_EVT) |
| CASE_RETURN_STR(BTA_AV_PROTECT_RSP_EVT) |
| CASE_RETURN_STR(BTA_AV_RC_OPEN_EVT) |
| CASE_RETURN_STR(BTA_AV_RC_CLOSE_EVT) |
| CASE_RETURN_STR(BTA_AV_RC_BROWSE_OPEN_EVT) |
| CASE_RETURN_STR(BTA_AV_RC_BROWSE_CLOSE_EVT) |
| CASE_RETURN_STR(BTA_AV_REMOTE_CMD_EVT) |
| CASE_RETURN_STR(BTA_AV_REMOTE_RSP_EVT) |
| CASE_RETURN_STR(BTA_AV_VENDOR_CMD_EVT) |
| CASE_RETURN_STR(BTA_AV_VENDOR_RSP_EVT) |
| CASE_RETURN_STR(BTA_AV_RECONFIG_EVT) |
| CASE_RETURN_STR(BTA_AV_SUSPEND_EVT) |
| CASE_RETURN_STR(BTA_AV_PENDING_EVT) |
| CASE_RETURN_STR(BTA_AV_META_MSG_EVT) |
| CASE_RETURN_STR(BTA_AV_REJECT_EVT) |
| CASE_RETURN_STR(BTA_AV_RC_FEAT_EVT) |
| CASE_RETURN_STR(BTA_AV_OFFLOAD_START_RSP_EVT) |
| CASE_RETURN_STR(BTIF_AV_CONNECT_REQ_EVT) |
| CASE_RETURN_STR(BTIF_AV_DISCONNECT_REQ_EVT) |
| CASE_RETURN_STR(BTIF_AV_START_STREAM_REQ_EVT) |
| CASE_RETURN_STR(BTIF_AV_STOP_STREAM_REQ_EVT) |
| CASE_RETURN_STR(BTIF_AV_SUSPEND_STREAM_REQ_EVT) |
| CASE_RETURN_STR(BTIF_AV_SINK_CONFIG_REQ_EVT) |
| CASE_RETURN_STR(BTIF_AV_ACL_DISCONNECTED) |
| CASE_RETURN_STR(BTIF_AV_OFFLOAD_START_REQ_EVT) |
| CASE_RETURN_STR(BTIF_AV_AVRCP_OPEN_EVT) |
| CASE_RETURN_STR(BTIF_AV_AVRCP_CLOSE_EVT) |
| CASE_RETURN_STR(BTIF_AV_AVRCP_REMOTE_PLAY_EVT) |
| default: |
| return "UNKNOWN_EVENT"; |
| } |
| } |
| |
| BtifAvEvent::BtifAvEvent(uint32_t event, const void* p_data, size_t data_length) |
| : event_(event), data_(nullptr), data_length_(0) { |
| DeepCopy(event, p_data, data_length); |
| } |
| |
| BtifAvEvent::BtifAvEvent(const BtifAvEvent& other) |
| : event_(0), data_(nullptr), data_length_(0) { |
| *this = other; |
| } |
| |
| BtifAvEvent& BtifAvEvent::operator=(const BtifAvEvent& other) { |
| DeepFree(); |
| DeepCopy(other.Event(), other.Data(), other.DataLength()); |
| return *this; |
| } |
| |
| BtifAvEvent::~BtifAvEvent() { DeepFree(); } |
| |
| std::string BtifAvEvent::ToString() const { |
| return BtifAvEvent::EventName(event_); |
| } |
| |
| std::string BtifAvEvent::EventName(uint32_t event) { |
| std::string name = dump_av_sm_event_name((btif_av_sm_event_t)event); |
| std::stringstream ss_value; |
| ss_value << "(0x" << std::hex << event << ")"; |
| return name + ss_value.str(); |
| } |
| |
| void BtifAvEvent::DeepCopy(uint32_t event, const void* p_data, |
| size_t data_length) { |
| event_ = event; |
| data_length_ = data_length; |
| if (data_length == 0) { |
| data_ = nullptr; |
| } else { |
| data_ = osi_malloc(data_length_); |
| memcpy(data_, p_data, data_length); |
| } |
| |
| switch (event) { |
| case BTA_AV_META_MSG_EVT: { |
| CHECK(data_length >= sizeof(tBTA_AV)); |
| const tBTA_AV* av_src = (const tBTA_AV*)p_data; |
| tBTA_AV* av_dest = (tBTA_AV*)data_; |
| if (av_src->meta_msg.p_data && av_src->meta_msg.len) { |
| av_dest->meta_msg.p_data = (uint8_t*)osi_calloc(av_src->meta_msg.len); |
| memcpy(av_dest->meta_msg.p_data, av_src->meta_msg.p_data, |
| av_src->meta_msg.len); |
| } |
| |
| if (av_src->meta_msg.p_msg) { |
| av_dest->meta_msg.p_msg = (tAVRC_MSG*)osi_calloc(sizeof(tAVRC_MSG)); |
| memcpy(av_dest->meta_msg.p_msg, av_src->meta_msg.p_msg, |
| sizeof(tAVRC_MSG)); |
| |
| tAVRC_MSG* p_msg_src = av_src->meta_msg.p_msg; |
| tAVRC_MSG* p_msg_dest = av_dest->meta_msg.p_msg; |
| |
| if ((p_msg_src->hdr.opcode == AVRC_OP_VENDOR) && |
| (p_msg_src->vendor.p_vendor_data && p_msg_src->vendor.vendor_len)) { |
| p_msg_dest->vendor.p_vendor_data = |
| (uint8_t*)osi_calloc(p_msg_src->vendor.vendor_len); |
| memcpy(p_msg_dest->vendor.p_vendor_data, |
| p_msg_src->vendor.p_vendor_data, p_msg_src->vendor.vendor_len); |
| } |
| } |
| } break; |
| |
| default: |
| break; |
| } |
| } |
| |
| void BtifAvEvent::DeepFree() { |
| switch (event_) { |
| case BTA_AV_META_MSG_EVT: { |
| tBTA_AV* av = (tBTA_AV*)data_; |
| osi_free_and_reset((void**)&av->meta_msg.p_data); |
| |
| if (av->meta_msg.p_msg) { |
| if (av->meta_msg.p_msg->hdr.opcode == AVRC_OP_VENDOR) { |
| osi_free(av->meta_msg.p_msg->vendor.p_vendor_data); |
| } |
| osi_free_and_reset((void**)&av->meta_msg.p_msg); |
| } |
| } break; |
| |
| default: |
| break; |
| } |
| |
| osi_free_and_reset((void**)&data_); |
| data_length_ = 0; |
| } |
| |
| BtifAvPeer::BtifAvPeer(const RawAddress& peer_address, uint8_t peer_sep, |
| tBTA_AV_HNDL bta_handle, uint8_t peer_id) |
| : peer_address_(peer_address), |
| peer_sep_(peer_sep), |
| bta_handle_(bta_handle), |
| peer_id_(peer_id), |
| state_machine_(*this), |
| av_open_on_rc_timer_(nullptr), |
| edr_(0), |
| flags_(0), |
| self_initiated_connection_(false) {} |
| |
| BtifAvPeer::~BtifAvPeer() { alarm_free(av_open_on_rc_timer_); } |
| |
| std::string BtifAvPeer::FlagsToString() const { |
| std::string result; |
| |
| if (flags_ & BtifAvPeer::kFlagLocalSuspendPending) { |
| if (!result.empty()) result += "|"; |
| result += "LOCAL_SUSPEND_PENDING"; |
| } |
| if (flags_ & BtifAvPeer::kFlagRemoteSuspend) { |
| if (!result.empty()) result += "|"; |
| result += "REMOTE_SUSPEND"; |
| } |
| if (flags_ & BtifAvPeer::kFlagPendingStart) { |
| if (!result.empty()) result += "|"; |
| result += "PENDING_START"; |
| } |
| if (flags_ & BtifAvPeer::kFlagPendingStop) { |
| if (!result.empty()) result += "|"; |
| result += "PENDING_STOP"; |
| } |
| if (result.empty()) result = "None"; |
| |
| return base::StringPrintf("0x%x(%s)", flags_, result.c_str()); |
| } |
| |
| bt_status_t BtifAvPeer::Init() { |
| alarm_free(av_open_on_rc_timer_); |
| av_open_on_rc_timer_ = alarm_new("btif_av_peer.av_open_on_rc_timer"); |
| |
| state_machine_.Start(); |
| return BT_STATUS_SUCCESS; |
| } |
| |
| void BtifAvPeer::Cleanup() { |
| state_machine_.Quit(); |
| alarm_free(av_open_on_rc_timer_); |
| av_open_on_rc_timer_ = nullptr; |
| } |
| |
| bool BtifAvPeer::CanBeDeleted() const { |
| return ( |
| (state_machine_.StateId() == BtifAvStateMachine::kStateIdle) && |
| (state_machine_.PreviousStateId() != BtifAvStateMachine::kStateInvalid)); |
| } |
| |
| bool BtifAvPeer::IsConnected() const { |
| int state = state_machine_.StateId(); |
| return ((state == BtifAvStateMachine::kStateOpened) || |
| (state == BtifAvStateMachine::kStateStarted)); |
| } |
| |
| bool BtifAvPeer::IsStreaming() const { |
| int state = state_machine_.StateId(); |
| return (state == BtifAvStateMachine::kStateStarted); |
| } |
| |
| BtifAvSource::~BtifAvSource() { CleanupAllPeers(); } |
| |
| bt_status_t BtifAvSource::Init( |
| btav_source_callbacks_t* callbacks, |
| const std::vector<btav_a2dp_codec_config_t>& codec_priorities) { |
| if (enabled_) return BT_STATUS_SUCCESS; |
| |
| CleanupAllPeers(); |
| max_connected_peers_ = |
| osi_property_get_int32(kBtifAvMaxConnectedAudioDevices.c_str(), |
| kDefaultMaxConnectedAudioDevices); |
| callbacks_ = callbacks; |
| codec_priorities_ = codec_priorities; |
| bta_av_co_init(codec_priorities_); |
| |
| if (!btif_a2dp_source_startup()) { |
| return BT_STATUS_FAIL; // Already running |
| } |
| btif_enable_service(BTA_A2DP_SOURCE_SERVICE_ID); |
| enabled_ = true; |
| return BT_STATUS_SUCCESS; |
| } |
| |
| void BtifAvSource::Cleanup() { |
| if (!enabled_) return; |
| |
| btif_queue_cleanup(UUID_SERVCLASS_AUDIO_SOURCE); |
| do_in_jni_thread(FROM_HERE, base::Bind(&btif_a2dp_source_shutdown)); |
| |
| btif_disable_service(BTA_A2DP_SOURCE_SERVICE_ID); |
| CleanupAllPeers(); |
| |
| callbacks_ = nullptr; |
| enabled_ = false; |
| } |
| |
| BtifAvPeer* BtifAvSource::FindPeer(const RawAddress& peer_address) { |
| auto it = peers_.find(peer_address); |
| if (it != peers_.end()) return it->second; |
| return nullptr; |
| } |
| |
| BtifAvPeer* BtifAvSource::FindPeerByHandle(tBTA_AV_HNDL bta_handle) { |
| for (auto it : peers_) { |
| BtifAvPeer* peer = it.second; |
| if (peer->BtaHandle() == bta_handle) { |
| return peer; |
| } |
| } |
| return nullptr; |
| } |
| |
| BtifAvPeer* BtifAvSource::FindPeerByPeerId(uint8_t peer_id) { |
| for (auto it : peers_) { |
| BtifAvPeer* peer = it.second; |
| if (peer->PeerId() == peer_id) { |
| return peer; |
| } |
| } |
| return nullptr; |
| } |
| |
| BtifAvPeer* BtifAvSource::FindOrCreatePeer(const RawAddress& peer_address, |
| tBTA_AV_HNDL bta_handle) { |
| BtifAvPeer* peer = FindPeer(peer_address); |
| if (peer != nullptr) return peer; |
| |
| // Find next availabie Peer ID to use |
| uint8_t peer_id; |
| for (peer_id = kPeerIdMin; peer_id < kPeerIdMax; peer_id++) { |
| if (FindPeerByPeerId(peer_id) == nullptr) break; |
| } |
| if (peer_id == kPeerIdMax) { |
| BTIF_TRACE_ERROR( |
| "%s: Cannot create peer for peer_address=%s : " |
| "cannot allocate unique Peer ID", |
| __PRETTY_FUNCTION__, peer_address.ToString().c_str()); |
| return nullptr; |
| } |
| |
| // Get the BTA Handle (if known) |
| if (bta_handle == kBtaHandleUnknown) { |
| auto it = peer_id2bta_handle_.find(peer_id); |
| if (it != peer_id2bta_handle_.end()) { |
| bta_handle = it->second; |
| } |
| } |
| |
| peer = new BtifAvPeer(peer_address, AVDT_TSEP_SNK, bta_handle, peer_id); |
| peers_.insert(std::make_pair(peer_address, peer)); |
| peer->Init(); |
| return peer; |
| } |
| |
| bool BtifAvSource::AllowedToConnect(const RawAddress& peer_address) const { |
| int connected = 0; |
| |
| // Count peers that are in the process of connecting or already connected |
| for (auto it : peers_) { |
| const BtifAvPeer* peer = it.second; |
| switch (peer->StateMachine().StateId()) { |
| case BtifAvStateMachine::kStateOpening: |
| case BtifAvStateMachine::kStateOpened: |
| case BtifAvStateMachine::kStateStarted: |
| if (peer->PeerAddress() == peer_address) { |
| return true; // Already connected or accounted for |
| } |
| connected++; |
| break; |
| default: |
| break; |
| } |
| } |
| return (connected < max_connected_peers_); |
| } |
| |
| bool BtifAvSource::DeletePeer(const RawAddress& peer_address) { |
| auto it = peers_.find(peer_address); |
| if (it == peers_.end()) return false; |
| BtifAvPeer* peer = it->second; |
| peer->Cleanup(); |
| peers_.erase(it); |
| delete peer; |
| return true; |
| } |
| |
| void BtifAvSource::DeleteIdlePeers() { |
| for (auto it = peers_.begin(); it != peers_.end();) { |
| BtifAvPeer* peer = it->second; |
| auto prev_it = it++; |
| if (!peer->CanBeDeleted()) continue; |
| BTIF_TRACE_WARNING("%s: Deleting idle peer: %s", __func__, |
| peer->PeerAddress().ToString().c_str()); |
| peer->Cleanup(); |
| peers_.erase(prev_it); |
| delete peer; |
| } |
| } |
| |
| void BtifAvSource::CleanupAllPeers() { |
| while (!peers_.empty()) { |
| auto it = peers_.begin(); |
| BtifAvPeer* peer = it->second; |
| peer->Cleanup(); |
| peers_.erase(it); |
| delete peer; |
| } |
| } |
| |
| void BtifAvSource::RegisterAllBtaHandles() { |
| for (int peer_id = kPeerIdMin; peer_id < kPeerIdMax; peer_id++) { |
| BTA_AvRegister(BTA_AV_CHNL_AUDIO, kBtifAvSourceServiceName.c_str(), peer_id, |
| nullptr, UUID_SERVCLASS_AUDIO_SOURCE); |
| } |
| } |
| |
| void BtifAvSource::DeregisterAllBtaHandles() { |
| for (auto it : peer_id2bta_handle_) { |
| tBTA_AV_HNDL bta_handle = it.second; |
| BTA_AvDeregister(bta_handle); |
| } |
| peer_id2bta_handle_.clear(); |
| } |
| |
| void BtifAvSource::BtaHandleRegistered(uint8_t peer_id, |
| tBTA_AV_HNDL bta_handle) { |
| peer_id2bta_handle_.insert(std::make_pair(peer_id, bta_handle)); |
| |
| // Set the BTA Handle for the Peer (if exists) |
| BtifAvPeer* peer = FindPeerByPeerId(peer_id); |
| if (peer != nullptr) { |
| peer->SetBtaHandle(bta_handle); |
| } |
| } |
| |
| BtifAvSink::~BtifAvSink() { CleanupAllPeers(); } |
| |
| bt_status_t BtifAvSink::Init(btav_sink_callbacks_t* callbacks) { |
| if (enabled_) return BT_STATUS_SUCCESS; |
| |
| CleanupAllPeers(); |
| max_connected_peers_ = |
| osi_property_get_int32(kBtifAvMaxConnectedAudioDevices.c_str(), |
| kDefaultMaxConnectedAudioDevices); |
| callbacks_ = callbacks; |
| |
| if (!btif_a2dp_sink_startup()) { |
| return BT_STATUS_FAIL; // Already running |
| } |
| btif_enable_service(BTA_A2DP_SINK_SERVICE_ID); |
| enabled_ = true; |
| return BT_STATUS_SUCCESS; |
| } |
| |
| void BtifAvSink::Cleanup() { |
| if (!enabled_) return; |
| |
| btif_queue_cleanup(UUID_SERVCLASS_AUDIO_SINK); |
| do_in_jni_thread(FROM_HERE, base::Bind(&btif_a2dp_sink_shutdown)); |
| |
| btif_disable_service(BTA_A2DP_SINK_SERVICE_ID); |
| CleanupAllPeers(); |
| |
| callbacks_ = nullptr; |
| enabled_ = false; |
| } |
| |
| BtifAvPeer* BtifAvSink::FindPeer(const RawAddress& peer_address) { |
| auto it = peers_.find(peer_address); |
| if (it != peers_.end()) return it->second; |
| return nullptr; |
| } |
| |
| BtifAvPeer* BtifAvSink::FindPeerByHandle(tBTA_AV_HNDL bta_handle) { |
| for (auto it : peers_) { |
| BtifAvPeer* peer = it.second; |
| if (peer->BtaHandle() == bta_handle) { |
| return peer; |
| } |
| } |
| return nullptr; |
| } |
| |
| BtifAvPeer* BtifAvSink::FindPeerByPeerId(uint8_t peer_id) { |
| for (auto it : peers_) { |
| BtifAvPeer* peer = it.second; |
| if (peer->PeerId() == peer_id) { |
| return peer; |
| } |
| } |
| return nullptr; |
| } |
| |
| BtifAvPeer* BtifAvSink::FindOrCreatePeer(const RawAddress& peer_address, |
| tBTA_AV_HNDL bta_handle) { |
| BtifAvPeer* peer = FindPeer(peer_address); |
| if (peer != nullptr) return peer; |
| |
| // Find next availabie Peer ID to use |
| uint8_t peer_id; |
| for (peer_id = kPeerIdMin; peer_id < kPeerIdMax; peer_id++) { |
| if (FindPeerByPeerId(peer_id) == nullptr) break; |
| } |
| if (peer_id == kPeerIdMax) { |
| BTIF_TRACE_ERROR( |
| "%s: Cannot create peer for peer_address=%s : " |
| "cannot allocate unique Peer ID", |
| __PRETTY_FUNCTION__, peer_address.ToString().c_str()); |
| return nullptr; |
| } |
| |
| // Get the BTA Handle (if known) |
| if (bta_handle == kBtaHandleUnknown) { |
| auto it = peer_id2bta_handle_.find(peer_id); |
| if (it != peer_id2bta_handle_.end()) { |
| bta_handle = it->second; |
| } |
| } |
| |
| peer = new BtifAvPeer(peer_address, AVDT_TSEP_SRC, bta_handle, peer_id); |
| peers_.insert(std::make_pair(peer_address, peer)); |
| peer->Init(); |
| return peer; |
| } |
| |
| bool BtifAvSink::AllowedToConnect(const RawAddress& peer_address) const { |
| int connected = 0; |
| |
| // Count peers that are in the process of connecting or already connected |
| for (auto it : peers_) { |
| const BtifAvPeer* peer = it.second; |
| switch (peer->StateMachine().StateId()) { |
| case BtifAvStateMachine::kStateOpening: |
| case BtifAvStateMachine::kStateOpened: |
| case BtifAvStateMachine::kStateStarted: |
| if (peer->PeerAddress() == peer_address) { |
| return true; // Already connected or accounted for |
| } |
| connected++; |
| break; |
| default: |
| break; |
| } |
| } |
| return (connected < max_connected_peers_); |
| } |
| |
| bool BtifAvSink::DeletePeer(const RawAddress& peer_address) { |
| auto it = peers_.find(peer_address); |
| if (it == peers_.end()) return false; |
| BtifAvPeer* peer = it->second; |
| peer->Cleanup(); |
| peers_.erase(it); |
| delete peer; |
| return true; |
| } |
| |
| void BtifAvSink::DeleteIdlePeers() { |
| for (auto it = peers_.begin(); it != peers_.end();) { |
| BtifAvPeer* peer = it->second; |
| auto prev_it = it++; |
| if (!peer->CanBeDeleted()) continue; |
| BTIF_TRACE_WARNING("%s: Deleting idle peer: %s", __func__, |
| peer->PeerAddress().ToString().c_str()); |
| peer->Cleanup(); |
| peers_.erase(prev_it); |
| delete peer; |
| } |
| } |
| |
| void BtifAvSink::CleanupAllPeers() { |
| while (!peers_.empty()) { |
| auto it = peers_.begin(); |
| BtifAvPeer* peer = it->second; |
| peer->Cleanup(); |
| peers_.erase(it); |
| delete peer; |
| } |
| } |
| |
| void BtifAvSink::RegisterAllBtaHandles() { |
| for (int peer_id = kPeerIdMin; peer_id < kPeerIdMax; peer_id++) { |
| BTA_AvRegister(BTA_AV_CHNL_AUDIO, kBtifAvSinkServiceName.c_str(), peer_id, |
| bta_av_sink_media_callback, UUID_SERVCLASS_AUDIO_SINK); |
| } |
| } |
| |
| void BtifAvSink::DeregisterAllBtaHandles() { |
| for (auto it : peer_id2bta_handle_) { |
| tBTA_AV_HNDL bta_handle = it.second; |
| BTA_AvDeregister(bta_handle); |
| } |
| peer_id2bta_handle_.clear(); |
| } |
| |
| void BtifAvSink::BtaHandleRegistered(uint8_t peer_id, tBTA_AV_HNDL bta_handle) { |
| peer_id2bta_handle_.insert(std::make_pair(peer_id, bta_handle)); |
| |
| // Set the BTA Handle for the Peer (if exists) |
| BtifAvPeer* peer = FindPeerByPeerId(peer_id); |
| if (peer != nullptr) { |
| peer->SetBtaHandle(bta_handle); |
| } |
| } |
| |
| void BtifAvStateMachine::StateIdle::OnEnter() { |
| BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str()); |
| |
| peer_.SetEdr(0); |
| peer_.ClearAllFlags(); |
| |
| // Stop A2DP if this is the active peer |
| RawAddress active_peer = RawAddress::kEmpty; |
| if (peer_.IsSink()) { |
| active_peer = btif_av_source.ActivePeer(); |
| } else if (peer_.IsSource()) { |
| active_peer = btif_av_sink.ActivePeer(); |
| } |
| if (peer_.PeerAddress() == active_peer || active_peer.IsEmpty()) { |
| btif_a2dp_on_idle(); |
| } |
| |
| // Reset the active peer if this was the active peer and |
| // the Idle state was reentered |
| if (peer_.PeerAddress() == active_peer && !active_peer.IsEmpty() && |
| peer_.CanBeDeleted()) { |
| if (peer_.IsSink()) { |
| btif_av_source.SetActivePeer(RawAddress::kEmpty); |
| } else if (peer_.IsSource()) { |
| btif_av_sink.SetActivePeer(RawAddress::kEmpty); |
| } |
| } |
| |
| // Delete peers that are re-entering the Idle state |
| if (peer_.IsSink()) { |
| do_in_jni_thread(FROM_HERE, base::Bind(&BtifAvSource::DeleteIdlePeers, |
| base::Unretained(&btif_av_source))); |
| } else if (peer_.IsSource()) { |
| do_in_jni_thread(FROM_HERE, base::Bind(&BtifAvSink::DeleteIdlePeers, |
| base::Unretained(&btif_av_sink))); |
| } |
| } |
| |
| void BtifAvStateMachine::StateIdle::OnExit() { |
| BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str()); |
| } |
| |
| bool BtifAvStateMachine::StateIdle::ProcessEvent(uint32_t event, void* p_data) { |
| BTIF_TRACE_DEBUG("%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str(), |
| peer_.FlagsToString().c_str()); |
| |
| switch (event) { |
| case BTA_AV_ENABLE_EVT: |
| break; |
| |
| case BTIF_AV_STOP_STREAM_REQ_EVT: |
| case BTIF_AV_SUSPEND_STREAM_REQ_EVT: |
| case BTIF_AV_ACL_DISCONNECTED: |
| // Ignore. Just re-enter Idle so the peer can be deleted |
| peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateIdle); |
| break; |
| |
| case BTIF_AV_DISCONNECT_REQ_EVT: |
| if (peer_.BtaHandle() != kBtaHandleUnknown) { |
| BTA_AvClose(peer_.BtaHandle()); |
| if (peer_.IsSource()) { |
| BTA_AvCloseRc(peer_.BtaHandle()); |
| } |
| } |
| // Re-enter Idle so the peer can be deleted |
| peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateIdle); |
| break; |
| |
| case BTIF_AV_CONNECT_REQ_EVT: |
| case BTA_AV_PENDING_EVT: { |
| bool can_connect = true; |
| peer_.SetSelfInitiatedConnection(event == BTIF_AV_CONNECT_REQ_EVT); |
| // Check whether connection is allowed |
| if (peer_.IsSink()) { |
| can_connect = btif_av_source.AllowedToConnect(peer_.PeerAddress()); |
| if (!can_connect) src_disconnect_sink(peer_.PeerAddress()); |
| } else if (peer_.IsSource()) { |
| can_connect = btif_av_sink.AllowedToConnect(peer_.PeerAddress()); |
| if (!can_connect) sink_disconnect_src(peer_.PeerAddress()); |
| } |
| if (!can_connect) { |
| BTIF_TRACE_ERROR( |
| "%s: Cannot connect to peer %s: too many connected " |
| "peers", |
| __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str()); |
| if (peer_.SelfInitiatedConnection()) { |
| btif_queue_advance(); |
| } |
| break; |
| } |
| BTA_AvOpen(peer_.PeerAddress(), peer_.BtaHandle(), true, |
| BTA_SEC_AUTHENTICATE, peer_.LocalUuidServiceClass()); |
| peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateOpening); |
| } break; |
| case BTIF_AV_AVRCP_OPEN_EVT: |
| case BTA_AV_RC_OPEN_EVT: { |
| // IOP_FIX: Jabra 620 only does AVRCP Open without AV Open whenever it |
| // connects. So as per the AV WP, an AVRCP connection cannot exist |
| // without an AV connection. Therefore, we initiate an AV connection |
| // if an RC_OPEN_EVT is received when we are in AV_CLOSED state. |
| // We initiate the AV connection after a small 3s timeout to avoid any |
| // collisions from the headsets, as some headsets initiate the AVRCP |
| // connection first and then immediately initiate the AV connection |
| // |
| // TODO: We may need to do this only on an AVRCP Play. FixMe |
| BTIF_TRACE_WARNING("%s: Peer %s : event=%s received without AV", |
| __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str()); |
| |
| bool can_connect = true; |
| // Check whether connection is allowed |
| if (peer_.IsSink()) { |
| can_connect = btif_av_source.AllowedToConnect(peer_.PeerAddress()); |
| if (!can_connect) src_disconnect_sink(peer_.PeerAddress()); |
| } else if (peer_.IsSource()) { |
| can_connect = btif_av_sink.AllowedToConnect(peer_.PeerAddress()); |
| if (!can_connect) sink_disconnect_src(peer_.PeerAddress()); |
| } |
| if (!can_connect) { |
| BTIF_TRACE_ERROR( |
| "%s: Cannot connect to peer %s: too many connected " |
| "peers", |
| __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str()); |
| break; |
| } |
| if (btif_av_source.Enabled()) { |
| alarm_set_on_mloop( |
| peer_.AvOpenOnRcTimer(), BtifAvPeer::kTimeoutAvOpenOnRcMs, |
| btif_av_source_initiate_av_open_timer_timeout, &peer_); |
| } else if (btif_av_sink.Enabled()) { |
| alarm_set_on_mloop(peer_.AvOpenOnRcTimer(), |
| BtifAvPeer::kTimeoutAvOpenOnRcMs, |
| btif_av_sink_initiate_av_open_timer_timeout, &peer_); |
| } |
| if (event == BTA_AV_RC_OPEN_EVT) { |
| btif_rc_handler(event, (tBTA_AV*)p_data); |
| } |
| } break; |
| |
| case BTA_AV_RC_BROWSE_OPEN_EVT: |
| btif_rc_handler(event, (tBTA_AV*)p_data); |
| break; |
| |
| // In case Signalling channel is not down and remote started Streaming |
| // Procedure, we have to handle Config and Open event in Idle state. |
| // We hit these scenarios while running PTS test case for AVRCP Controller. |
| case BTIF_AV_SINK_CONFIG_REQ_EVT: { |
| btif_av_sink_config_req_t req; |
| // Copy to avoid alignment problems |
| memcpy(&req, p_data, sizeof(req)); |
| |
| BTIF_TRACE_WARNING( |
| "%s: Peer %s : event=%s sample_rate=%d channel_count=%d", |
| __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str(), req.sample_rate, |
| req.channel_count); |
| if (btif_av_sink.Enabled()) { |
| HAL_CBACK(btif_av_sink.Callbacks(), audio_config_cb, req.peer_bd, |
| req.sample_rate, req.channel_count); |
| } |
| } break; |
| |
| case BTA_AV_OPEN_EVT: { |
| tBTA_AV* p_bta_data = (tBTA_AV*)p_data; |
| btav_connection_state_t state; |
| int av_state; |
| tBTA_AV_STATUS status = p_bta_data->open.status; |
| bool can_connect = true; |
| |
| BTIF_TRACE_WARNING( |
| "%s: Peer %s : event=%s flags=%s status=%d(%s) edr=0x%x", |
| __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str(), peer_.FlagsToString().c_str(), |
| status, (status == BTA_AV_SUCCESS) ? "SUCCESS" : "FAILED", |
| p_bta_data->open.edr); |
| |
| if (p_bta_data->open.status == BTA_AV_SUCCESS) { |
| state = BTAV_CONNECTION_STATE_CONNECTED; |
| av_state = BtifAvStateMachine::kStateOpened; |
| peer_.SetEdr(p_bta_data->open.edr); |
| CHECK(peer_.PeerSep() == p_bta_data->open.sep); |
| // Check whether connection is allowed |
| if (peer_.IsSink()) { |
| can_connect = btif_av_source.AllowedToConnect(peer_.PeerAddress()); |
| if (!can_connect) src_disconnect_sink(peer_.PeerAddress()); |
| } else if (peer_.IsSource()) { |
| can_connect = btif_av_sink.AllowedToConnect(peer_.PeerAddress()); |
| if (!can_connect) sink_disconnect_src(peer_.PeerAddress()); |
| } |
| } else { |
| state = BTAV_CONNECTION_STATE_DISCONNECTED; |
| av_state = BtifAvStateMachine::kStateIdle; |
| } |
| |
| if (!can_connect) { |
| BTIF_TRACE_ERROR( |
| "%s: Cannot connect to peer %s: too many connected " |
| "peers", |
| __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str()); |
| } else { |
| // Report the connection state to the application |
| btif_report_connection_state(peer_.PeerAddress(), state); |
| // Change state to Open/Idle based on the status |
| peer_.StateMachine().TransitionTo(av_state); |
| if (peer_.IsSink()) { |
| // If queued PLAY command, send it now |
| btif_rc_check_handle_pending_play( |
| p_bta_data->open.bd_addr, |
| (p_bta_data->open.status == BTA_AV_SUCCESS)); |
| } else if (peer_.IsSource() && |
| (p_bta_data->open.status == BTA_AV_SUCCESS)) { |
| // Bring up AVRCP connection as well |
| BTA_AvOpenRc(peer_.BtaHandle()); |
| } |
| } |
| btif_queue_advance(); |
| } break; |
| |
| case BTA_AV_REMOTE_CMD_EVT: |
| case BTA_AV_VENDOR_CMD_EVT: |
| case BTA_AV_META_MSG_EVT: |
| case BTA_AV_RC_FEAT_EVT: |
| case BTA_AV_REMOTE_RSP_EVT: |
| btif_rc_handler(event, (tBTA_AV*)p_data); |
| break; |
| |
| case BTIF_AV_AVRCP_CLOSE_EVT: |
| case BTA_AV_RC_CLOSE_EVT: { |
| BTIF_TRACE_DEBUG("%s: Peer %s : event=%s : Stopping AV timer", |
| __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str()); |
| alarm_cancel(peer_.AvOpenOnRcTimer()); |
| |
| if (event == BTA_AV_RC_CLOSE_EVT) { |
| btif_rc_handler(event, (tBTA_AV*)p_data); |
| } |
| } break; |
| |
| case BTIF_AV_OFFLOAD_START_REQ_EVT: |
| BTIF_TRACE_ERROR("%s: Peer %s : event=%s: stream is not Opened", |
| __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str()); |
| btif_a2dp_on_offload_started(BTA_AV_FAIL); |
| break; |
| |
| default: |
| BTIF_TRACE_WARNING("%s: Peer %s : Unhandled event=%s", |
| __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str()); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void BtifAvStateMachine::StateOpening::OnEnter() { |
| BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str()); |
| |
| // Inform the application that we are entering connecting state |
| btif_report_connection_state(peer_.PeerAddress(), |
| BTAV_CONNECTION_STATE_CONNECTING); |
| } |
| |
| void BtifAvStateMachine::StateOpening::OnExit() { |
| BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str()); |
| } |
| |
| bool BtifAvStateMachine::StateOpening::ProcessEvent(uint32_t event, |
| void* p_data) { |
| BTIF_TRACE_DEBUG("%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str(), |
| peer_.FlagsToString().c_str()); |
| |
| switch (event) { |
| case BTIF_AV_STOP_STREAM_REQ_EVT: |
| case BTIF_AV_SUSPEND_STREAM_REQ_EVT: |
| break; // Ignore |
| |
| case BTIF_AV_ACL_DISCONNECTED: |
| // ACL Disconnected needs to be handled only in Opening state, because |
| // it is in an intermediate state. In other states we can handle |
| // incoming/outgoing connect/disconnect requests. |
| BTIF_TRACE_WARNING( |
| "%s: Peer %s : event=%s: transitioning to Idle due to ACL Disconnect", |
| __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str()); |
| btif_report_connection_state(peer_.PeerAddress(), |
| BTAV_CONNECTION_STATE_DISCONNECTED); |
| peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateIdle); |
| if (peer_.SelfInitiatedConnection()) { |
| btif_queue_advance(); |
| } |
| break; |
| case BTA_AV_REJECT_EVT: |
| BTIF_TRACE_WARNING("%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str(), |
| peer_.FlagsToString().c_str()); |
| btif_report_connection_state(peer_.PeerAddress(), |
| BTAV_CONNECTION_STATE_DISCONNECTED); |
| peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateIdle); |
| if (peer_.SelfInitiatedConnection()) { |
| btif_queue_advance(); |
| } |
| break; |
| |
| case BTA_AV_OPEN_EVT: { |
| tBTA_AV* p_bta_data = (tBTA_AV*)p_data; |
| btav_connection_state_t state; |
| int av_state; |
| tBTA_AV_STATUS status = p_bta_data->open.status; |
| |
| BTIF_TRACE_WARNING( |
| "%s: Peer %s : event=%s flags=%s status=%d(%s) edr=0x%x", |
| __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str(), peer_.FlagsToString().c_str(), |
| status, (status == BTA_AV_SUCCESS) ? "SUCCESS" : "FAILED", |
| p_bta_data->open.edr); |
| |
| if (p_bta_data->open.status == BTA_AV_SUCCESS) { |
| state = BTAV_CONNECTION_STATE_CONNECTED; |
| av_state = BtifAvStateMachine::kStateOpened; |
| peer_.SetEdr(p_bta_data->open.edr); |
| CHECK(peer_.PeerSep() == p_bta_data->open.sep); |
| } else { |
| if (btif_rc_is_connected_peer(peer_.PeerAddress())) { |
| // Disconnect the AVRCP connection, in case the A2DP connectiton |
| // failed for any reason. |
| BTIF_TRACE_WARNING("%s: Peer %s : Disconnecting AVRCP", |
| __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str()); |
| uint8_t peer_handle = |
| btif_rc_get_connected_peer_handle(peer_.PeerAddress()); |
| if (peer_handle != BTRC_HANDLE_NONE) { |
| BTA_AvCloseRc(peer_handle); |
| } |
| } |
| state = BTAV_CONNECTION_STATE_DISCONNECTED; |
| av_state = BtifAvStateMachine::kStateIdle; |
| } |
| |
| // Report the connection state to the application |
| btif_report_connection_state(peer_.PeerAddress(), state); |
| // Change state to Open/Idle based on the status |
| peer_.StateMachine().TransitionTo(av_state); |
| if (peer_.IsSink()) { |
| // If queued PLAY command, send it now |
| btif_rc_check_handle_pending_play( |
| p_bta_data->open.bd_addr, |
| (p_bta_data->open.status == BTA_AV_SUCCESS)); |
| } else if (peer_.IsSource() && |
| (p_bta_data->open.status == BTA_AV_SUCCESS)) { |
| // Bring up AVRCP connection as well |
| BTA_AvOpenRc(peer_.BtaHandle()); |
| } |
| if (peer_.SelfInitiatedConnection()) { |
| btif_queue_advance(); |
| } |
| } break; |
| |
| case BTIF_AV_SINK_CONFIG_REQ_EVT: { |
| btif_av_sink_config_req_t req; |
| // Copy to avoid alignment problems |
| memcpy(&req, p_data, sizeof(req)); |
| |
| BTIF_TRACE_WARNING( |
| "%s: Peer %s : event=%s sample_rate=%d channel_count=%d", |
| __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str(), req.sample_rate, |
| req.channel_count); |
| if (peer_.IsSource() && btif_av_sink.Enabled()) { |
| HAL_CBACK(btif_av_sink.Callbacks(), audio_config_cb, |
| peer_.PeerAddress(), req.sample_rate, req.channel_count); |
| } |
| } break; |
| |
| case BTIF_AV_CONNECT_REQ_EVT: { |
| // The device has moved already to Opening, hence don't report the |
| // connection state. |
| BTIF_TRACE_WARNING( |
| "%s: Peer %s : event=%s : device is already connecting, " |
| "ignore Connect request", |
| __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str()); |
| btif_queue_advance(); |
| } break; |
| |
| case BTA_AV_PENDING_EVT: { |
| // The device has moved already to Opening, hence don't report the |
| // connection state. |
| BTIF_TRACE_WARNING( |
| "%s: Peer %s : event=%s : device is already connecting, " |
| "ignore incoming request", |
| __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str()); |
| } break; |
| |
| case BTIF_AV_OFFLOAD_START_REQ_EVT: |
| BTIF_TRACE_ERROR("%s: Peer %s : event=%s: stream is not Opened", |
| __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str()); |
| btif_a2dp_on_offload_started(BTA_AV_FAIL); |
| break; |
| |
| case BTA_AV_CLOSE_EVT: |
| btif_a2dp_on_stopped(nullptr); |
| btif_report_connection_state(peer_.PeerAddress(), |
| BTAV_CONNECTION_STATE_DISCONNECTED); |
| peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateIdle); |
| if (peer_.SelfInitiatedConnection()) { |
| btif_queue_advance(); |
| } |
| break; |
| |
| case BTIF_AV_DISCONNECT_REQ_EVT: |
| btif_report_connection_state(peer_.PeerAddress(), |
| BTAV_CONNECTION_STATE_DISCONNECTED); |
| BTA_AvClose(peer_.BtaHandle()); |
| peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateIdle); |
| if (peer_.SelfInitiatedConnection()) { |
| btif_queue_advance(); |
| } |
| break; |
| |
| CHECK_RC_EVENT(event, (tBTA_AV*)p_data); |
| |
| default: |
| BTIF_TRACE_WARNING("%s: Peer %s : Unhandled event=%s", |
| __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str()); |
| return false; |
| } |
| return true; |
| } |
| |
| void BtifAvStateMachine::StateOpened::OnEnter() { |
| BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str()); |
| |
| peer_.ClearFlags(BtifAvPeer::kFlagPendingStart | |
| BtifAvPeer::kFlagPendingStop); |
| |
| // Set the active peer if the first connected device |
| if (peer_.IsSink() && btif_av_source.ActivePeer().IsEmpty()) { |
| if (!btif_av_source.SetActivePeer(peer_.PeerAddress())) { |
| BTIF_TRACE_ERROR("%s: Error setting %s as active Sink peer", __func__, |
| peer_.PeerAddress().ToString().c_str()); |
| } |
| } |
| if (peer_.IsSource() && btif_av_sink.ActivePeer().IsEmpty()) { |
| if (!btif_av_sink.SetActivePeer(peer_.PeerAddress())) { |
| BTIF_TRACE_ERROR("%s: Error setting %s as active Source peer", __func__, |
| peer_.PeerAddress().ToString().c_str()); |
| } |
| } |
| } |
| |
| void BtifAvStateMachine::StateOpened::OnExit() { |
| BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str()); |
| |
| peer_.ClearFlags(BtifAvPeer::kFlagPendingStart); |
| } |
| |
| bool BtifAvStateMachine::StateOpened::ProcessEvent(uint32_t event, |
| void* p_data) { |
| tBTA_AV* p_av = (tBTA_AV*)p_data; |
| |
| BTIF_TRACE_DEBUG("%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str(), |
| peer_.FlagsToString().c_str()); |
| |
| if ((event == BTA_AV_REMOTE_CMD_EVT) && |
| peer_.CheckFlags(BtifAvPeer::kFlagRemoteSuspend) && |
| (p_av->remote_cmd.rc_id == AVRC_ID_PLAY)) { |
| BTIF_TRACE_EVENT("%s: Peer %s : Resetting remote suspend flag on RC PLAY", |
| __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str()); |
| peer_.ClearFlags(BtifAvPeer::kFlagRemoteSuspend); |
| } |
| |
| switch (event) { |
| case BTIF_AV_STOP_STREAM_REQ_EVT: |
| case BTIF_AV_SUSPEND_STREAM_REQ_EVT: |
| case BTIF_AV_ACL_DISCONNECTED: |
| break; // Ignore |
| |
| case BTIF_AV_START_STREAM_REQ_EVT: |
| if (peer_.IsSink()) { |
| btif_a2dp_source_setup_codec(peer_.PeerAddress()); |
| } |
| BTA_AvStart(peer_.BtaHandle()); |
| peer_.SetFlags(BtifAvPeer::kFlagPendingStart); |
| break; |
| |
| case BTA_AV_START_EVT: { |
| BTIF_TRACE_WARNING( |
| "%s: Peer %s : event=%s status=%d suspending=%d " |
| "initiator=%d flags=%s", |
| __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str(), p_av->start.status, |
| p_av->start.suspending, p_av->start.initiator, |
| peer_.FlagsToString().c_str()); |
| |
| if ((p_av->start.status == BTA_SUCCESS) && p_av->start.suspending) |
| return true; |
| |
| // If remote tries to start A2DP when DUT is A2DP Source, then Suspend. |
| // If A2DP is Sink and call is active, then disconnect the AVDTP channel. |
| if (peer_.IsSink() && !peer_.CheckFlags(BtifAvPeer::kFlagPendingStart)) { |
| BTIF_TRACE_WARNING("%s: Peer %s : trigger Suspend as remote initiated", |
| __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str()); |
| btif_av_source_dispatch_sm_event(peer_.PeerAddress(), |
| BTIF_AV_SUSPEND_STREAM_REQ_EVT); |
| } |
| |
| // If peer is A2DP Source, we do not want to ACK commands on UIPC |
| if (peer_.IsSink() && |
| btif_a2dp_on_started( |
| peer_.PeerAddress(), &p_av->start, |
| peer_.CheckFlags(BtifAvPeer::kFlagPendingStart))) { |
| // Only clear pending flag after acknowledgement |
| peer_.ClearFlags(BtifAvPeer::kFlagPendingStart); |
| } |
| |
| // Remain in Open state if status failed |
| if (p_av->start.status != BTA_AV_SUCCESS) return false; |
| |
| if (peer_.IsSource()) { |
| // Remove flush state, ready for streaming |
| btif_a2dp_sink_set_rx_flush(false); |
| } |
| |
| // Change state to Started, send acknowledgement if start is pending |
| if (peer_.IsSink() && peer_.CheckFlags(BtifAvPeer::kFlagPendingStart)) { |
| btif_a2dp_on_started(peer_.PeerAddress(), nullptr, true); |
| // Pending start flag will be cleared when exit current state |
| } |
| peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateStarted); |
| |
| } break; |
| |
| case BTIF_AV_DISCONNECT_REQ_EVT: |
| BTA_AvClose(peer_.BtaHandle()); |
| if (peer_.IsSource()) { |
| BTA_AvCloseRc(peer_.BtaHandle()); |
| } |
| |
| // Inform the application that we are disconnecting |
| btif_report_connection_state(peer_.PeerAddress(), |
| BTAV_CONNECTION_STATE_DISCONNECTING); |
| |
| // Wait in closing state until fully closed |
| peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateClosing); |
| break; |
| |
| case BTA_AV_CLOSE_EVT: |
| // AVDTP link is closed |
| btif_a2dp_on_stopped(nullptr); |
| |
| // Inform the application that we are disconnected |
| btif_report_connection_state(peer_.PeerAddress(), |
| BTAV_CONNECTION_STATE_DISCONNECTED); |
| |
| // Change state to Idle, send acknowledgement if start is pending |
| if (peer_.CheckFlags(BtifAvPeer::kFlagPendingStart)) { |
| btif_a2dp_command_ack(A2DP_CTRL_ACK_FAILURE); |
| // Pending start flag will be cleared when exit current state |
| } |
| peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateIdle); |
| break; |
| |
| case BTA_AV_RECONFIG_EVT: |
| if (peer_.CheckFlags(BtifAvPeer::kFlagPendingStart) && |
| (p_av->reconfig.status == BTA_AV_SUCCESS)) { |
| BTIF_TRACE_WARNING( |
| "%s : Peer %s : Reconfig done - calling BTA_AvStart()", |
| __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str()); |
| BTA_AvStart(peer_.BtaHandle()); |
| } else if (peer_.CheckFlags(BtifAvPeer::kFlagPendingStart)) { |
| peer_.ClearFlags(BtifAvPeer::kFlagPendingStart); |
| btif_a2dp_command_ack(A2DP_CTRL_ACK_FAILURE); |
| } |
| break; |
| |
| case BTIF_AV_CONNECT_REQ_EVT: { |
| BTIF_TRACE_WARNING("%s: Peer %s : Ignore %s for same device", |
| __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str()); |
| btif_queue_advance(); |
| } break; |
| |
| case BTIF_AV_OFFLOAD_START_REQ_EVT: |
| BTIF_TRACE_ERROR("%s: Peer %s : event=%s: stream is not Opened", |
| __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str()); |
| btif_a2dp_on_offload_started(BTA_AV_FAIL); |
| break; |
| |
| case BTIF_AV_AVRCP_REMOTE_PLAY_EVT: |
| if (peer_.CheckFlags(BtifAvPeer::kFlagRemoteSuspend)) { |
| BTIF_TRACE_EVENT( |
| "%s: Peer %s : Resetting remote suspend flag on RC PLAY", |
| __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str()); |
| peer_.ClearFlags(BtifAvPeer::kFlagRemoteSuspend); |
| } |
| break; |
| |
| CHECK_RC_EVENT(event, (tBTA_AV*)p_data); |
| |
| default: |
| BTIF_TRACE_WARNING("%s: Peer %s : Unhandled event=%s", |
| __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str()); |
| return false; |
| } |
| return true; |
| } |
| |
| void BtifAvStateMachine::StateStarted::OnEnter() { |
| BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str()); |
| |
| // We are again in started state, clear any remote suspend flags |
| peer_.ClearFlags(BtifAvPeer::kFlagRemoteSuspend); |
| |
| // Report that we have entered the Streaming stage. Usually, this should |
| // be followed by focus grant. See update_audio_focus_state() |
| btif_report_audio_state(peer_.PeerAddress(), BTAV_AUDIO_STATE_STARTED); |
| } |
| |
| void BtifAvStateMachine::StateStarted::OnExit() { |
| BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str()); |
| } |
| |
| bool BtifAvStateMachine::StateStarted::ProcessEvent(uint32_t event, |
| void* p_data) { |
| tBTA_AV* p_av = (tBTA_AV*)p_data; |
| |
| BTIF_TRACE_DEBUG("%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str(), |
| peer_.FlagsToString().c_str()); |
| |
| switch (event) { |
| case BTIF_AV_ACL_DISCONNECTED: |
| break; // Ignore |
| |
| case BTIF_AV_START_STREAM_REQ_EVT: |
| // We were remotely started, just ACK back the local request |
| if (peer_.IsSink()) |
| btif_a2dp_on_started(peer_.PeerAddress(), nullptr, true); |
| break; |
| |
| // FIXME -- use suspend = true always to work around issue with BTA AV |
| case BTIF_AV_STOP_STREAM_REQ_EVT: |
| case BTIF_AV_SUSPEND_STREAM_REQ_EVT: |
| BTIF_TRACE_WARNING("%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str(), |
| peer_.FlagsToString().c_str()); |
| // Set pending flag to ensure the BTIF task is not trying to restart |
| // the stream while suspend is in progress. |
| peer_.SetFlags(BtifAvPeer::kFlagLocalSuspendPending); |
| |
| // If we were remotely suspended but suspend locally, local suspend |
| // always overrides. |
| peer_.ClearFlags(BtifAvPeer::kFlagRemoteSuspend); |
| |
| if (peer_.IsSink()) { |
| // Immediately stop transmission of frames while suspend is pending |
| btif_a2dp_source_set_tx_flush(true); |
| } else if (peer_.IsSource()) { |
| btif_a2dp_on_stopped(nullptr); |
| } |
| BTA_AvStop(peer_.BtaHandle(), true); |
| break; |
| |
| case BTIF_AV_DISCONNECT_REQ_EVT: |
| BTIF_TRACE_WARNING("%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str(), |
| peer_.FlagsToString().c_str()); |
| |
| // Request AVDTP to close |
| BTA_AvClose(peer_.BtaHandle()); |
| if (peer_.IsSource()) { |
| BTA_AvCloseRc(peer_.BtaHandle()); |
| } |
| |
| // Inform the application that we are disconnecting |
| btif_report_connection_state(peer_.PeerAddress(), |
| BTAV_CONNECTION_STATE_DISCONNECTING); |
| |
| // Wait in closing state until fully closed |
| peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateClosing); |
| break; |
| |
| case BTA_AV_SUSPEND_EVT: |
| BTIF_TRACE_WARNING( |
| "%s: Peer %s : event=%s status=%d initiator=%d flags=%s", |
| __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str(), p_av->suspend.status, |
| p_av->suspend.initiator, peer_.FlagsToString().c_str()); |
| |
| // A2DP suspended, stop A2DP encoder/decoder until resumed |
| btif_a2dp_on_suspended(&p_av->suspend); |
| |
| // If not successful, remain in current state |
| if (p_av->suspend.status != BTA_AV_SUCCESS) { |
| peer_.ClearFlags(BtifAvPeer::kFlagLocalSuspendPending); |
| |
| if (peer_.IsSink()) { |
| // Suspend failed, reset back tx flush state |
| btif_a2dp_source_set_tx_flush(false); |
| } |
| return false; |
| } |
| |
| if (p_av->suspend.initiator != true) { |
| // Remote suspend, notify HAL and await audioflinger to |
| // suspend/stop stream. |
| // |
| // Set remote suspend flag to block media task from restarting |
| // stream only if we did not already initiate a local suspend. |
| if (!peer_.CheckFlags(BtifAvPeer::kFlagLocalSuspendPending)) |
| peer_.SetFlags(BtifAvPeer::kFlagRemoteSuspend); |
| |
| btif_report_audio_state(peer_.PeerAddress(), |
| BTAV_AUDIO_STATE_REMOTE_SUSPEND); |
| } else { |
| btif_report_audio_state(peer_.PeerAddress(), BTAV_AUDIO_STATE_STOPPED); |
| } |
| |
| peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateOpened); |
| |
| // Suspend completed and state changed, clear pending status |
| peer_.ClearFlags(BtifAvPeer::kFlagLocalSuspendPending); |
| break; |
| |
| case BTA_AV_STOP_EVT: |
| BTIF_TRACE_WARNING("%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str(), |
| peer_.FlagsToString().c_str()); |
| |
| peer_.SetFlags(BtifAvPeer::kFlagPendingStop); |
| btif_a2dp_on_stopped(&p_av->suspend); |
| |
| btif_report_audio_state(peer_.PeerAddress(), BTAV_AUDIO_STATE_STOPPED); |
| |
| // If stop was successful, change state to Open |
| if (p_av->suspend.status == BTA_AV_SUCCESS) |
| peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateOpened); |
| |
| break; |
| |
| case BTA_AV_CLOSE_EVT: |
| BTIF_TRACE_WARNING("%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str(), |
| peer_.FlagsToString().c_str()); |
| |
| peer_.SetFlags(BtifAvPeer::kFlagPendingStop); |
| |
| // AVDTP link is closed |
| btif_a2dp_on_stopped(nullptr); |
| |
| // Inform the application that we are disconnected |
| btif_report_connection_state(peer_.PeerAddress(), |
| BTAV_CONNECTION_STATE_DISCONNECTED); |
| |
| peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateIdle); |
| break; |
| |
| case BTIF_AV_OFFLOAD_START_REQ_EVT: |
| BTA_AvOffloadStart(peer_.BtaHandle()); |
| break; |
| |
| case BTA_AV_OFFLOAD_START_RSP_EVT: |
| btif_a2dp_on_offload_started(p_av->status); |
| break; |
| |
| CHECK_RC_EVENT(event, (tBTA_AV*)p_data); |
| |
| default: |
| BTIF_TRACE_WARNING("%s: Peer %s : Unhandled event=%s", |
| __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str()); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void BtifAvStateMachine::StateClosing::OnEnter() { |
| BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str()); |
| |
| if (peer_.IsSink()) { |
| // Immediately stop transmission of frames |
| btif_a2dp_source_set_tx_flush(true); |
| // Wait for Audio Flinger to stop A2DP |
| } else if (peer_.IsSource()) { |
| btif_a2dp_sink_set_rx_flush(true); |
| } |
| } |
| |
| void BtifAvStateMachine::StateClosing::OnExit() { |
| BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str()); |
| } |
| |
| bool BtifAvStateMachine::StateClosing::ProcessEvent(uint32_t event, |
| void* p_data) { |
| BTIF_TRACE_DEBUG("%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str(), |
| peer_.FlagsToString().c_str()); |
| |
| switch (event) { |
| case BTIF_AV_SUSPEND_STREAM_REQ_EVT: |
| case BTIF_AV_ACL_DISCONNECTED: |
| break; // Ignore |
| |
| case BTA_AV_STOP_EVT: |
| case BTIF_AV_STOP_STREAM_REQ_EVT: |
| btif_a2dp_on_stopped(nullptr); |
| break; |
| |
| case BTA_AV_CLOSE_EVT: |
| // Inform the application that we are disconnecting |
| btif_report_connection_state(peer_.PeerAddress(), |
| BTAV_CONNECTION_STATE_DISCONNECTED); |
| |
| peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateIdle); |
| break; |
| |
| // Handle the RC_CLOSE event for the cleanup |
| case BTA_AV_RC_CLOSE_EVT: |
| btif_rc_handler(event, (tBTA_AV*)p_data); |
| break; |
| |
| // Handle the RC_BROWSE_CLOSE event for testing |
| case BTA_AV_RC_BROWSE_CLOSE_EVT: |
| btif_rc_handler(event, (tBTA_AV*)p_data); |
| break; |
| |
| case BTIF_AV_OFFLOAD_START_REQ_EVT: |
| BTIF_TRACE_ERROR("%s: Peer %s : event=%s: stream is not Opened", |
| __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str()); |
| btif_a2dp_on_offload_started(BTA_AV_FAIL); |
| break; |
| |
| default: |
| BTIF_TRACE_WARNING("%s: Peer %s : Unhandled event=%s", |
| __PRETTY_FUNCTION__, |
| peer_.PeerAddress().ToString().c_str(), |
| BtifAvEvent::EventName(event).c_str()); |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * Timer to trigger AV Open on the Source if the remote Sink device establishes |
| * AVRCP connection without AV connection. The timer is needed to interoperate |
| * with headsets that do establish AV after AVRCP connection. |
| */ |
| static void btif_av_source_initiate_av_open_timer_timeout(void* data) { |
| BtifAvPeer* peer = (BtifAvPeer*)data; |
| |
| BTIF_TRACE_DEBUG("%s: Peer %s", __func__, |
| peer->PeerAddress().ToString().c_str()); |
| |
| // Check if AVRCP is connected to the peer |
| if (!btif_rc_is_connected_peer(peer->PeerAddress())) { |
| BTIF_TRACE_ERROR("%s: AVRCP peer %s is not connected", __func__, |
| peer->PeerAddress().ToString().c_str()); |
| return; |
| } |
| |
| // Connect to the AVRCP peer |
| if (btif_av_source.Enabled() && |
| btif_av_source.FindPeer(peer->PeerAddress()) == peer) { |
| BTIF_TRACE_DEBUG("%s: Connecting to AVRCP peer %s", __func__, |
| peer->PeerAddress().ToString().c_str()); |
| btif_av_source_dispatch_sm_event(peer->PeerAddress(), |
| BTIF_AV_CONNECT_REQ_EVT); |
| } |
| } |
| |
| /** |
| * Timer to trigger AV Open on the Sink if the remote Source device establishes |
| * AVRCP connection without AV connection. |
| */ |
| static void btif_av_sink_initiate_av_open_timer_timeout(void* data) { |
| BtifAvPeer* peer = (BtifAvPeer*)data; |
| |
| BTIF_TRACE_DEBUG("%s: Peer %s", __func__, |
| peer->PeerAddress().ToString().c_str()); |
| |
| // Check if AVRCP is connected to the peer |
| if (!btif_rc_is_connected_peer(peer->PeerAddress())) { |
| BTIF_TRACE_ERROR("%s: AVRCP peer %s is not connected", __func__, |
| peer->PeerAddress().ToString().c_str()); |
| return; |
| } |
| |
| // Connect to the AVRCP peer |
| if (btif_av_sink.Enabled() && |
| btif_av_sink.FindPeer(peer->PeerAddress()) == peer) { |
| BTIF_TRACE_DEBUG("%s: Connecting to AVRCP peer %s", __func__, |
| peer->PeerAddress().ToString().c_str()); |
| btif_av_sink_dispatch_sm_event(peer->PeerAddress(), |
| BTIF_AV_CONNECT_REQ_EVT); |
| } |
| } |
| |
| /** |
| * Report the A2DP connection state |
| * |
| * @param peer_address the peer address |
| * @param state the connection state |
| */ |
| static void btif_report_connection_state(const RawAddress& peer_address, |
| btav_connection_state_t state) { |
| BTIF_TRACE_WARNING("%s: peer_address=%s state=%d", __func__, |
| peer_address.ToString().c_str(), state); |
| |
| if (btif_av_source.Enabled()) { |
| HAL_CBACK(btif_av_source.Callbacks(), connection_state_cb, peer_address, |
| state); |
| } else if (btif_av_sink.Enabled()) { |
| HAL_CBACK(btif_av_sink.Callbacks(), connection_state_cb, peer_address, |
| state); |
| } |
| } |
| |
| /** |
| * Report the audio state of the A2DP connection. |
| * The state is updated when either the remote ends starts streaming |
| * (Started state) or whenever it transitions out of Started state |
| * (to Opened or Streaming state). |
| * |
| * @param peer_address the peer address |
| * @param state the audio state |
| */ |
| static void btif_report_audio_state(const RawAddress& peer_address, |
| btav_audio_state_t state) { |
| BTIF_TRACE_WARNING("%s: peer_address=%s state=%d", __func__, |
| peer_address.ToString().c_str(), state); |
| |
| if (btif_av_source.Enabled()) { |
| HAL_CBACK(btif_av_source.Callbacks(), audio_state_cb, peer_address, state); |
| } else if (btif_av_sink.Enabled()) { |
| HAL_CBACK(btif_av_sink.Callbacks(), audio_state_cb, peer_address, state); |
| } |
| } |
| |
| void btif_av_report_source_codec_state( |
| const RawAddress& peer_address, |
| const btav_a2dp_codec_config_t& codec_config, |
| const std::vector<btav_a2dp_codec_config_t>& codecs_local_capabilities, |
| const std::vector<btav_a2dp_codec_config_t>& |
| codecs_selectable_capabilities) { |
| BTIF_TRACE_EVENT("%s: peer_address=%s", __func__, |
| peer_address.ToString().c_str()); |
| if (btif_av_source.Enabled()) { |
| HAL_CBACK(btif_av_source.Callbacks(), audio_config_cb, peer_address, |
| codec_config, codecs_local_capabilities, |
| codecs_selectable_capabilities); |
| } |
| } |
| |
| /** |
| * Process BTIF or BTA AV or BTA AVRCP events. The processing is done on the |
| * JNI thread. |
| * |
| * @param peer_sep the corresponding peer's SEP: AVDT_TSEP_SRC if the peer |
| * is A2DP Source, or AVDT_TSEP_SNK if the peer is A2DP Sink. |
| * @param peer_address the peer address if known, otherwise RawAddress::kEmpty |
| * @param bta_handle the BTA handle for the peer if known, otherwise |
| * kBtaHandleUnknown |
| * @param btif_av_event the corresponding event |
| */ |
| static void btif_av_handle_event(uint8_t peer_sep, |
| const RawAddress& peer_address, |
| tBTA_AV_HNDL bta_handle, |
| const BtifAvEvent& btif_av_event) { |
| BtifAvPeer* peer = nullptr; |
| BTIF_TRACE_EVENT("%s: peer_sep=%s (%d) peer_address=%s handle=%d event=%s", |
| __func__, (peer_sep == AVDT_TSEP_SRC) ? "Source" : "Sink", |
| peer_sep, peer_address.ToString().c_str(), bta_handle, |
| btif_av_event.ToString().c_str()); |
| |
| // Find the peer |
| if (peer_address != RawAddress::kEmpty) { |
| if (peer_sep == AVDT_TSEP_SNK) { |
| peer = btif_av_source.FindOrCreatePeer(peer_address, bta_handle); |
| } else if (peer_sep == AVDT_TSEP_SRC) { |
| peer = btif_av_sink.FindOrCreatePeer(peer_address, bta_handle); |
| } |
| } else if (bta_handle != kBtaHandleUnknown) { |
| if (peer_sep == AVDT_TSEP_SNK) { |
| peer = btif_av_source.FindPeerByHandle(bta_handle); |
| } else if (peer_sep == AVDT_TSEP_SRC) { |
| peer = btif_av_sink.FindPeerByHandle(bta_handle); |
| } |
| } |
| if (peer == nullptr) { |
| BTIF_TRACE_ERROR( |
| "%s: Cannot find or create %s peer for peer_address=%s handle=%d : " |
| "event dropped: %s", |
| __func__, (peer_sep == AVDT_TSEP_SRC) ? "Source" : "Sink", |
| peer_address.ToString().c_str(), bta_handle, |
| btif_av_event.ToString().c_str()); |
| return; |
| } |
| |
| peer->StateMachine().ProcessEvent(btif_av_event.Event(), |
| btif_av_event.Data()); |
| } |
| |
| /** |
| * Process BTA AV or BTA AVRCP events. The processing is done on the JNI |
| * thread. |
| * |
| * @param peer_sep the corresponding peer's SEP: AVDT_TSEP_SRC if the peer |
| * is A2DP Source, or AVDT_TSEP_SNK if the peer is A2DP Sink. |
| * @param btif_av_event the corresponding event |
| */ |
| static void btif_av_handle_bta_av_event(uint8_t peer_sep, |
| const BtifAvEvent& btif_av_event) { |
| RawAddress peer_address = RawAddress::kEmpty; |
| tBTA_AV_HNDL bta_handle = kBtaHandleUnknown; |
| tBTA_AV_EVT event = btif_av_event.Event(); |
| tBTA_AV* p_data = (tBTA_AV*)btif_av_event.Data(); |
| |
| BTIF_TRACE_DEBUG("%s: peer_sep=%s (%d) event=%s", __func__, |
| (peer_sep == AVDT_TSEP_SRC) ? "Source" : "Sink", peer_sep, |
| btif_av_event.ToString().c_str()); |
| |
| switch (event) { |
| case BTA_AV_ENABLE_EVT: { |
| const tBTA_AV_ENABLE& enable = p_data->enable; |
| BTIF_TRACE_DEBUG("%s: features=0x%x", __func__, enable.features); |
| return; // Nothing to do |
| } |
| case BTA_AV_REGISTER_EVT: { |
| const tBTA_AV_REGISTER& registr = p_data->registr; |
| bta_handle = registr.hndl; |
| uint8_t peer_id = registr.app_id; // The PeerId is used as AppId |
| BTIF_TRACE_DEBUG("%s: handle=%d app_id=%d", __func__, bta_handle, |
| registr.app_id); |
| if (peer_sep == AVDT_TSEP_SNK) { |
| btif_av_source.BtaHandleRegistered(peer_id, bta_handle); |
| } else if (peer_sep == AVDT_TSEP_SRC) { |
| btif_av_sink.BtaHandleRegistered(peer_id, bta_handle); |
| } |
| return; // Nothing else to do |
| } |
| case BTA_AV_OPEN_EVT: { |
| const tBTA_AV_OPEN& open = p_data->open; |
| peer_address = open.bd_addr; |
| bta_handle = open.hndl; |
| break; |
| } |
| case BTA_AV_CLOSE_EVT: { |
| const tBTA_AV_CLOSE& close = p_data->close; |
| bta_handle = close.hndl; |
| break; |
| } |
| case BTA_AV_START_EVT: { |
| const tBTA_AV_START& start = p_data->start; |
| bta_handle = start.hndl; |
| break; |
| } |
| case BTA_AV_SUSPEND_EVT: |
| case BTA_AV_STOP_EVT: { |
| const tBTA_AV_SUSPEND& suspend = p_data->suspend; |
| bta_handle = suspend.hndl; |
| break; |
| } |
| case BTA_AV_PROTECT_REQ_EVT: { |
| const tBTA_AV_PROTECT_REQ& protect_req = p_data->protect_req; |
| bta_handle = protect_req.hndl; |
| break; |
| } |
| case BTA_AV_PROTECT_RSP_EVT: { |
| const tBTA_AV_PROTECT_RSP& protect_rsp = p_data->protect_rsp; |
| bta_handle = protect_rsp.hndl; |
| break; |
| } |
| case BTA_AV_RC_OPEN_EVT: { |
| const tBTA_AV_RC_OPEN& rc_open = p_data->rc_open; |
| peer_address = rc_open.peer_addr; |
| break; |
| } |
| case BTA_AV_RC_CLOSE_EVT: { |
| const tBTA_AV_RC_CLOSE& rc_close = p_data->rc_close; |
| peer_address = rc_close.peer_addr; |
| break; |
| } |
| case BTA_AV_RC_BROWSE_OPEN_EVT: { |
| const tBTA_AV_RC_BROWSE_OPEN& rc_browse_open = p_data->rc_browse_open; |
| peer_address = rc_browse_open.peer_addr; |
| break; |
| } |
| case BTA_AV_RC_BROWSE_CLOSE_EVT: { |
| const tBTA_AV_RC_BROWSE_CLOSE& rc_browse_close = p_data->rc_browse_close; |
| peer_address = rc_browse_close.peer_addr; |
| break; |
| } |
| case BTA_AV_REMOTE_CMD_EVT: |
| case BTA_AV_REMOTE_RSP_EVT: |
| case BTA_AV_VENDOR_CMD_EVT: |
| case BTA_AV_VENDOR_RSP_EVT: |
| case BTA_AV_META_MSG_EVT: |
| case BTA_AV_OFFLOAD_START_RSP_EVT: { |
| // TODO: Might be wrong - this code will be removed once those |
| // events are received from the AVRCP module. |
| if (peer_sep == AVDT_TSEP_SNK) { |
| peer_address = btif_av_source.ActivePeer(); |
| } else if (peer_sep == AVDT_TSEP_SRC) { |
| peer_address = btif_av_sink.ActivePeer(); |
| } |
| break; |
| } |
| case BTA_AV_RECONFIG_EVT: { |
| const tBTA_AV_RECONFIG& reconfig = p_data->reconfig; |
| bta_handle = reconfig.hndl; |
| break; |
| } |
| case BTA_AV_PENDING_EVT: { |
| const tBTA_AV_PEND& pend = p_data->pend; |
| peer_address = pend.bd_addr; |
| break; |
| } |
| case BTA_AV_REJECT_EVT: { |
| const tBTA_AV_REJECT& reject = p_data->reject; |
| peer_address = reject.bd_addr; |
| bta_handle = reject.hndl; |
| break; |
| } |
| case BTA_AV_RC_FEAT_EVT: { |
| const tBTA_AV_RC_FEAT& rc_feat = p_data->rc_feat; |
| peer_address = rc_feat.peer_addr; |
| break; |
| } |
| } |
| BTIF_TRACE_DEBUG("%s: peer_address=%s handle=%d", __func__, |
| peer_address.ToString().c_str(), bta_handle); |
| |
| btif_av_handle_event(peer_sep, peer_address, bta_handle, btif_av_event); |
| } |
| |
| static void bta_av_source_callback(tBTA_AV_EVT event, tBTA_AV* p_data) { |
| BtifAvEvent btif_av_event(event, p_data, sizeof(tBTA_AV)); |
| BTIF_TRACE_EVENT("%s: event=%s", __func__, btif_av_event.ToString().c_str()); |
| |
| do_in_jni_thread(FROM_HERE, |
| base::Bind(&btif_av_handle_bta_av_event, |
| AVDT_TSEP_SNK /* peer_sep */, btif_av_event)); |
| } |
| |
| static void bta_av_sink_callback(tBTA_AV_EVT event, tBTA_AV* p_data) { |
| BtifAvEvent btif_av_event(event, p_data, sizeof(tBTA_AV)); |
| do_in_jni_thread(FROM_HERE, |
| base::Bind(&btif_av_handle_bta_av_event, |
| AVDT_TSEP_SRC /* peer_sep */, btif_av_event)); |
| } |
| |
| // TODO: All processing should be done on the JNI thread |
| static void bta_av_sink_media_callback(tBTA_AV_EVT event, |
| tBTA_AV_MEDIA* p_data) { |
| BTIF_TRACE_EVENT("%s: event=%d", __func__, event); |
| |
| switch (event) { |
| case BTA_AV_SINK_MEDIA_DATA_EVT: { |
| BtifAvPeer* peer = btif_av_sink_find_peer(btif_av_sink.ActivePeer()); |
| if (peer != nullptr) { |
| int state = peer->StateMachine().StateId(); |
| if ((state == BtifAvStateMachine::kStateStarted) || |
| (state == BtifAvStateMachine::kStateOpened)) { |
| uint8_t queue_len = btif_a2dp_sink_enqueue_buf((BT_HDR*)p_data); |
| BTIF_TRACE_DEBUG("%s: Packets in Sink queue %d", __func__, queue_len); |
| } |
| } |
| break; |
| } |
| case BTA_AV_SINK_MEDIA_CFG_EVT: { |
| btif_av_sink_config_req_t config_req; |
| |
| // Update the codec info of the A2DP Sink decoder |
| btif_a2dp_sink_update_decoder((uint8_t*)(p_data->avk_config.codec_info)); |
| |
| config_req.sample_rate = |
| A2DP_GetTrackSampleRate(p_data->avk_config.codec_info); |
| if (config_req.sample_rate == -1) { |
| APPL_TRACE_ERROR("%s: Cannot get the track frequency", __func__); |
| break; |
| } |
| config_req.channel_count = |
| A2DP_GetTrackChannelCount(p_data->avk_config.codec_info); |
| if (config_req.channel_count == -1) { |
| APPL_TRACE_ERROR("%s: Cannot get the channel count", __func__); |
| break; |
| } |
| config_req.peer_bd = p_data->avk_config.bd_addr; |
| BtifAvEvent btif_av_event(BTIF_AV_SINK_CONFIG_REQ_EVT, &config_req, |
| sizeof(config_req)); |
| do_in_jni_thread(FROM_HERE, base::Bind(&btif_av_handle_event, |
| AVDT_TSEP_SRC, // peer_sep |
| config_req.peer_bd, |
| kBtaHandleUnknown, btif_av_event)); |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| |
| // Initializes the AV interface for source mode |
| static bt_status_t init_src( |
| btav_source_callbacks_t* callbacks, |
| std::vector<btav_a2dp_codec_config_t> codec_priorities) { |
| BTIF_TRACE_EVENT("%s", __func__); |
| return btif_av_source.Init(callbacks, codec_priorities); |
| } |
| |
| // Initializes the AV interface for sink mode |
| static bt_status_t init_sink(btav_sink_callbacks_t* callbacks) { |
| BTIF_TRACE_EVENT("%s", __func__); |
| return btif_av_sink.Init(callbacks); |
| } |
| |
| // Updates the final focus state reported by components calling this module |
| static void update_audio_focus_state(int state) { |
| BTIF_TRACE_DEBUG("%s: state=%d", __func__, state); |
| btif_a2dp_sink_set_focus_state_req((btif_a2dp_sink_focus_state_t)state); |
| } |
| |
| // Updates the track gain (used for ducking). |
| static void update_audio_track_gain(float gain) { |
| BTIF_TRACE_DEBUG("%s: gain=%f", __func__, gain); |
| btif_a2dp_sink_set_audio_track_gain(gain); |
| } |
| |
| // Establishes the AV signalling channel with the remote headset |
| static bt_status_t connect_int(RawAddress* peer_address, uint16_t uuid) { |
| BTIF_TRACE_EVENT("%s: peer_address=%s uuid=0x%x", __func__, |
| peer_address->ToString().c_str(), uuid); |
| |
| BtifAvPeer* peer = nullptr; |
| if (uuid == UUID_SERVCLASS_AUDIO_SOURCE) { |
| peer = btif_av_source.FindOrCreatePeer(*peer_address, kBtaHandleUnknown); |
| if (peer == nullptr) { |
| return BT_STATUS_FAIL; |
| } |
| } else if (uuid == UUID_SERVCLASS_AUDIO_SINK) { |
| peer = btif_av_sink.FindOrCreatePeer(*peer_address, kBtaHandleUnknown); |
| if (peer == nullptr) { |
| return BT_STATUS_FAIL; |
| } |
| } |
| peer->StateMachine().ProcessEvent(BTIF_AV_CONNECT_REQ_EVT, nullptr); |
| return BT_STATUS_SUCCESS; |
| } |
| |
| // Set the active peer |
| static void set_active_peer_int(uint8_t peer_sep, |
| const RawAddress& peer_address) { |
| BTIF_TRACE_EVENT("%s: peer_sep=%s (%d) peer_address=%s", __func__, |
| (peer_sep == AVDT_TSEP_SRC) ? "Source" : "Sink", peer_sep, |
| peer_address.ToString().c_str()); |
| |
| BtifAvPeer* peer = nullptr; |
| if (peer_sep == AVDT_TSEP_SNK) { |
| peer = btif_av_source.FindPeer(peer_address); |
| if (peer != nullptr && peer->IsConnected()) { |
| if (!btif_av_source.SetActivePeer(peer_address)) { |
| BTIF_TRACE_ERROR("%s: Error setting %s as active Sink peer", __func__, |
| peer_address.ToString().c_str()); |
| } |
| return; |
| } |
| } |
| if (peer_sep == AVDT_TSEP_SRC) { |
| peer = btif_av_sink.FindPeer(peer_address); |
| if (peer != nullptr && peer->IsConnected()) { |
| if (!btif_av_sink.SetActivePeer(peer_address)) { |
| BTIF_TRACE_ERROR("%s: Error setting %s as active Source peer", __func__, |
| peer_address.ToString().c_str()); |
| } |
| return; |
| } |
| } |
| // If reached here, we could not set the active peer |
| BTIF_TRACE_ERROR("%s: Cannot set active %s peer to %s: peer not %s", __func__, |
| (peer_sep == AVDT_TSEP_SRC) ? "Source" : "Sink", |
| peer_address.ToString().c_str(), |
| (peer == nullptr) ? "found" : "connected"); |
| } |
| |
| static bt_status_t src_connect_sink(const RawAddress& peer_address) { |
| BTIF_TRACE_EVENT("%s: Peer %s", __func__, peer_address.ToString().c_str()); |
| |
| if (!btif_av_source.Enabled()) { |
| BTIF_TRACE_WARNING("%s: BTIF AV Source is not enabled", __func__); |
| return BT_STATUS_NOT_READY; |
| } |
| |
| RawAddress peer_address_copy(peer_address); |
| return btif_queue_connect(UUID_SERVCLASS_AUDIO_SOURCE, &peer_address_copy, |
| connect_int); |
| } |
| |
| static bt_status_t sink_connect_src(const RawAddress& peer_address) { |
| BTIF_TRACE_WARNING("%s: Peer %s", __func__, peer_address.ToString().c_str()); |
| |
| if (!btif_av_sink.Enabled()) { |
| BTIF_TRACE_WARNING("%s: BTIF AV Sink is not enabled", __func__); |
| return BT_STATUS_NOT_READY; |
| } |
| |
| RawAddress peer_address_copy(peer_address); |
| return btif_queue_connect(UUID_SERVCLASS_AUDIO_SINK, &peer_address_copy, |
| connect_int); |
| } |
| |
| static bt_status_t src_disconnect_sink(const RawAddress& peer_address) { |
| BTIF_TRACE_WARNING("%s: Peer %s", __func__, peer_address.ToString().c_str()); |
| |
| if (!btif_av_source.Enabled()) { |
| BTIF_TRACE_WARNING("%s: BTIF AV Source is not enabled", __func__); |
| return BT_STATUS_NOT_READY; |
| } |
| |
| BtifAvEvent btif_av_event(BTIF_AV_DISCONNECT_REQ_EVT, &peer_address, |
| sizeof(peer_address)); |
| return do_in_jni_thread( |
| FROM_HERE, base::Bind(&btif_av_handle_event, |
| AVDT_TSEP_SNK, // peer_sep |
| peer_address, kBtaHandleUnknown, btif_av_event)); |
| } |
| |
| static bt_status_t sink_disconnect_src(const RawAddress& peer_address) { |
| BTIF_TRACE_WARNING("%s: Peer %s", __func__, peer_address.ToString().c_str()); |
| |
| if (!btif_av_sink.Enabled()) { |
| BTIF_TRACE_WARNING("%s: BTIF AV Sink is not enabled", __func__); |
| return BT_STATUS_NOT_READY; |
| } |
| |
| BtifAvEvent btif_av_event(BTIF_AV_DISCONNECT_REQ_EVT, &peer_address, |
| sizeof(peer_address)); |
| return do_in_jni_thread( |
| FROM_HERE, base::Bind(&btif_av_handle_event, |
| AVDT_TSEP_SRC, // peer_sep |
| peer_address, kBtaHandleUnknown, btif_av_event)); |
| } |
| |
| static bt_status_t src_set_active_sink(const RawAddress& peer_address) { |
| BTIF_TRACE_EVENT("%s: Peer %s", __func__, peer_address.ToString().c_str()); |
| |
| if (!btif_av_source.Enabled()) { |
| BTIF_TRACE_WARNING("%s: BTIF AV Source is not enabled", __func__); |
| return BT_STATUS_NOT_READY; |
| } |
| |
| return do_in_jni_thread(FROM_HERE, base::Bind(&set_active_peer_int, |
| AVDT_TSEP_SNK, // peer_sep |
| peer_address)); |
| } |
| |
| static bt_status_t codec_config_src( |
| const RawAddress& peer_address, |
| std::vector<btav_a2dp_codec_config_t> codec_preferences) { |
| BTIF_TRACE_EVENT("%s", __func__); |
| |
| if (!btif_av_source.Enabled()) { |
| BTIF_TRACE_WARNING("%s: BTIF AV Source is not enabled", __func__); |
| return BT_STATUS_NOT_READY; |
| } |
| |
| for (auto cp : codec_preferences) { |
| BTIF_TRACE_DEBUG( |
| "%s: codec_type=%d codec_priority=%d " |
| "sample_rate=0x%x bits_per_sample=0x%x " |
| "channel_mode=0x%x codec_specific_1=%d " |
| "codec_specific_2=%d codec_specific_3=%d " |
| "codec_specific_4=%d", |
| __func__, cp.codec_type, cp.codec_priority, cp.sample_rate, |
| cp.bits_per_sample, cp.channel_mode, cp.codec_specific_1, |
| cp.codec_specific_2, cp.codec_specific_3, cp.codec_specific_4); |
| do_in_jni_thread( |
| FROM_HERE, base::Bind(&btif_a2dp_source_encoder_user_config_update_req, |
| peer_address, cp)); |
| } |
| |
| return BT_STATUS_SUCCESS; |
| } |
| |
| static void cleanup_src(void) { |
| BTIF_TRACE_EVENT("%s", __func__); |
| do_in_jni_thread(FROM_HERE, base::Bind(&BtifAvSource::Cleanup, |
| base::Unretained(&btif_av_source))); |
| } |
| |
| static void cleanup_sink(void) { |
| BTIF_TRACE_EVENT("%s", __func__); |
| do_in_jni_thread(FROM_HERE, base::Bind(&BtifAvSink::Cleanup, |
| base::Unretained(&btif_av_sink))); |
| } |
| |
| static const btav_source_interface_t bt_av_src_interface = { |
| sizeof(btav_source_interface_t), |
| init_src, |
| src_connect_sink, |
| src_disconnect_sink, |
| src_set_active_sink, |
| codec_config_src, |
| cleanup_src, |
| }; |
| |
| static const btav_sink_interface_t bt_av_sink_interface = { |
| sizeof(btav_sink_interface_t), init_sink, sink_connect_src, |
| sink_disconnect_src, cleanup_sink, update_audio_focus_state, |
| update_audio_track_gain, |
| }; |
| |
| RawAddress btif_av_source_active_peer(void) { |
| return btif_av_source.ActivePeer(); |
| } |
| |
| RawAddress btif_av_sink_active_peer(void) { return btif_av_sink.ActivePeer(); } |
| |
| bool btif_av_is_sink_enabled(void) { return btif_av_sink.Enabled(); } |
| |
| void btif_av_stream_start(void) { |
| btif_av_source_dispatch_sm_event(btif_av_source_active_peer(), |
| BTIF_AV_START_STREAM_REQ_EVT); |
| } |
| |
| void btif_av_stream_stop(void) { |
| // The active peer might have changed and we might be in the process |
| // of reconfiguring the stream. We need to stop the appopriate peer(s). |
| for (auto it : btif_av_source.Peers()) { |
| const BtifAvPeer* peer = it.second; |
| btif_av_source_dispatch_sm_event(peer->PeerAddress(), |
| BTIF_AV_STOP_STREAM_REQ_EVT); |
| } |
| } |
| |
| void btif_av_stream_suspend(void) { |
| // The active peer might have changed and we might be in the process |
| // of reconfiguring the stream. We need to suspend the appropriate peer(s). |
| for (auto it : btif_av_source.Peers()) { |
| const BtifAvPeer* peer = it.second; |
| btif_av_source_dispatch_sm_event(peer->PeerAddress(), |
| BTIF_AV_SUSPEND_STREAM_REQ_EVT); |
| } |
| } |
| |
| void btif_av_stream_start_offload(void) { |
| btif_av_source_dispatch_sm_event(btif_av_source_active_peer(), |
| BTIF_AV_OFFLOAD_START_REQ_EVT); |
| } |
| |
| bool btif_av_stream_ready(void) { |
| // Make sure the main adapter is enabled |
| if (btif_is_enabled() == 0) { |
| BTIF_TRACE_EVENT("%s: Main adapter is not enabled", __func__); |
| return false; |
| } |
| |
| BtifAvPeer* peer = btif_av_find_active_peer(); |
| if (peer == nullptr) { |
| BTIF_TRACE_WARNING("%s: No active peer found", __func__); |
| return false; |
| } |
| |
| int state = peer->StateMachine().StateId(); |
| BTIF_TRACE_DEBUG("%s: Peer %s : state=%d, flags=%s", __func__, |
| peer->PeerAddress().ToString().c_str(), state, |
| peer->FlagsToString().c_str()); |
| // check if we are remotely suspended or stop is pending |
| if (peer->CheckFlags(BtifAvPeer::kFlagRemoteSuspend | |
| BtifAvPeer::kFlagPendingStop)) { |
| return false; |
| } |
| |
| return (state == BtifAvStateMachine::kStateOpened); |
| } |
| |
| bool btif_av_stream_started_ready(void) { |
| BtifAvPeer* peer = btif_av_find_active_peer(); |
| if (peer == nullptr) { |
| BTIF_TRACE_WARNING("%s: No active peer found", __func__); |
| return false; |
| } |
| |
| int state = peer->StateMachine().StateId(); |
| bool ready = false; |
| if (peer->CheckFlags(BtifAvPeer::kFlagLocalSuspendPending | |
| BtifAvPeer::kFlagRemoteSuspend | |
| BtifAvPeer::kFlagPendingStop)) { |
| // Disallow media task to start if we have pending actions |
| ready = false; |
| } else { |
| ready = (state == BtifAvStateMachine::kStateStarted); |
| } |
| BTIF_TRACE_WARNING("%s: Peer %s : state=%d flags=%s ready=%d", __func__, |
| peer->PeerAddress().ToString().c_str(), state, |
| peer->FlagsToString().c_str(), ready); |
| |
| return ready; |
| } |
| |
| static void btif_av_source_dispatch_sm_event(const RawAddress& peer_address, |
| btif_av_sm_event_t event) { |
| BtifAvEvent btif_av_event(event, nullptr, 0); |
| BTIF_TRACE_EVENT("%s: peer_address=%s event=%s", __func__, |
| peer_address.ToString().c_str(), |
| btif_av_event.ToString().c_str()); |
| |
| do_in_jni_thread(FROM_HERE, |
| base::Bind(&btif_av_handle_event, |
| AVDT_TSEP_SNK, // peer_sep |
| peer_address, kBtaHandleUnknown, btif_av_event)); |
| } |
| |
| static void btif_av_sink_dispatch_sm_event(const RawAddress& peer_address, |
| btif_av_sm_event_t event) { |
| BtifAvEvent btif_av_event(event, nullptr, 0); |
| BTIF_TRACE_EVENT("%s: peer_address=%s event=%s", __func__, |
| peer_address.ToString().c_str(), |
| btif_av_event.ToString().c_str()); |
| |
| do_in_jni_thread(FROM_HERE, |
| base::Bind(&btif_av_handle_event, |
| AVDT_TSEP_SRC, // peer_sep |
| peer_address, kBtaHandleUnknown, btif_av_event)); |
| } |
| |
| bt_status_t btif_av_source_execute_service(bool enable) { |
| BTIF_TRACE_EVENT("%s: Source service: %s", __func__, |
| (enable) ? "enable" : "disable"); |
| |
| if (enable) { |
| // TODO: Removed BTA_SEC_AUTHORIZE since the Java/App does not |
| // handle this request in order to allow incoming connections to succeed. |
| // We need to put this back once support for this is added. |
| |
| // Added BTA_AV_FEAT_NO_SCO_SSPD - this ensures that the BTA does not |
| // auto-suspend av streaming on AG events(SCO or Call). The suspend shall |
| // be initiated by the app/audioflinger layers. |
| // Support for browsing for SDP record should work only if we enable BROWSE |
| // while registering. |
| tBTA_AV_FEAT features = BTA_AV_FEAT_RCTG | BTA_AV_FEAT_METADATA | |
| BTA_AV_FEAT_VENDOR | BTA_AV_FEAT_NO_SCO_SSPD; |
| #if (AVRC_ADV_CTRL_INCLUDED == TRUE) |
| features |= BTA_AV_FEAT_RCCT | BTA_AV_FEAT_ADV_CTRL | BTA_AV_FEAT_BROWSE; |
| #endif |
| BTA_AvEnable(BTA_SEC_AUTHENTICATE, features, bta_av_source_callback); |
| btif_av_source.RegisterAllBtaHandles(); |
| return BT_STATUS_SUCCESS; |
| } |
| |
| // Disable the service |
| btif_av_source.DeregisterAllBtaHandles(); |
| BTA_AvDisable(); |
| return BT_STATUS_SUCCESS; |
| } |
| |
| bt_status_t btif_av_sink_execute_service(bool enable) { |
| BTIF_TRACE_EVENT("%s: Sink service: %s", __func__, |
| (enable) ? "enable" : "disable"); |
| |
| if (enable) { |
| // Added BTA_AV_FEAT_NO_SCO_SSPD - this ensures that the BTA does not |
| // auto-suspend AV streaming on AG events (SCO or Call). The suspend shall |
| // be initiated by the app/audioflinger layers. |
| tBTA_AV_FEAT features = BTA_AV_FEAT_NO_SCO_SSPD | BTA_AV_FEAT_RCCT | |
| BTA_AV_FEAT_METADATA | BTA_AV_FEAT_VENDOR | |
| BTA_AV_FEAT_ADV_CTRL | BTA_AV_FEAT_RCTG | |
| BTA_AV_FEAT_BROWSE; |
| BTA_AvEnable(BTA_SEC_AUTHENTICATE, features, bta_av_sink_callback); |
| btif_av_sink.RegisterAllBtaHandles(); |
| return BT_STATUS_SUCCESS; |
| } |
| |
| // Disable the service |
| btif_av_sink.DeregisterAllBtaHandles(); |
| BTA_AvDisable(); |
| return BT_STATUS_SUCCESS; |
| } |
| |
| // Get the AV callback interface for A2DP source profile |
| const btav_source_interface_t* btif_av_get_src_interface(void) { |
| BTIF_TRACE_EVENT("%s", __func__); |
| return &bt_av_src_interface; |
| } |
| |
| // Get the AV callback interface for A2DP sink profile |
| const btav_sink_interface_t* btif_av_get_sink_interface(void) { |
| BTIF_TRACE_EVENT("%s", __func__); |
| return &bt_av_sink_interface; |
| } |
| |
| bool btif_av_is_connected(void) { |
| BtifAvPeer* peer = btif_av_find_active_peer(); |
| if (peer == nullptr) { |
| BTIF_TRACE_WARNING("%s: No active peer found", __func__); |
| return false; |
| } |
| |
| bool connected = peer->IsConnected(); |
| BTIF_TRACE_DEBUG("%s: Peer %s is %s", __func__, |
| peer->PeerAddress().ToString().c_str(), |
| (connected) ? "connected" : "not connected"); |
| return connected; |
| } |
| |
| uint8_t btif_av_get_peer_sep(void) { |
| BtifAvPeer* peer = btif_av_find_active_peer(); |
| if (peer == nullptr) { |
| BTIF_TRACE_WARNING("%s: No active peer found", __func__); |
| return AVDT_TSEP_SNK; |
| } |
| |
| uint8_t peer_sep = peer->PeerSep(); |
| BTIF_TRACE_DEBUG("%s: Peer %s SEP is %s (%d)", __func__, |
| peer->PeerAddress().ToString().c_str(), |
| (peer_sep == AVDT_TSEP_SRC) ? "Source" : "Sink", peer_sep); |
| return peer_sep; |
| } |
| |
| void btif_av_clear_remote_suspend_flag(void) { |
| BtifAvPeer* peer = btif_av_find_active_peer(); |
| if (peer == nullptr) { |
| BTIF_TRACE_WARNING("%s: No active peer found", __func__); |
| return; |
| } |
| |
| BTIF_TRACE_DEBUG("%s: Peer %s : flags=%s are cleared", __func__, |
| peer->PeerAddress().ToString().c_str(), |
| peer->FlagsToString().c_str()); |
| peer->ClearFlags(BtifAvPeer::kFlagRemoteSuspend); |
| } |
| |
| void btif_av_avrcp_event_open(const RawAddress& peer_address) { |
| // TODO: We need a better demultipexing mechanism whether the remote device |
| // is an A2DP Source or a Sink. |
| if (btif_av_source.Enabled()) { |
| BtifAvPeer* peer = |
| btif_av_source.FindOrCreatePeer(peer_address, kBtaHandleUnknown); |
| if (peer != nullptr) { |
| btif_av_source_dispatch_sm_event(peer_address, BTIF_AV_AVRCP_OPEN_EVT); |
| return; |
| } |
| } else if (btif_av_sink.Enabled()) { |
| BtifAvPeer* peer = |
| btif_av_sink.FindOrCreatePeer(peer_address, kBtaHandleUnknown); |
| if (peer != nullptr) { |
| btif_av_sink_dispatch_sm_event(peer_address, BTIF_AV_AVRCP_OPEN_EVT); |
| return; |
| } |
| } |
| BTIF_TRACE_ERROR("%s: event ignored: cannot find or create peer state for %s", |
| __func__, peer_address.ToString().c_str()); |
| } |
| |
| void btif_av_avrcp_event_close(const RawAddress& peer_address) { |
| // TODO: We need a better demultipexing mechanism whether the remote device |
| // is an A2DP Source or a Sink. |
| if (btif_av_source.Enabled()) { |
| btif_av_source_dispatch_sm_event(peer_address, BTIF_AV_AVRCP_CLOSE_EVT); |
| } else if (btif_av_sink.Enabled()) { |
| btif_av_sink_dispatch_sm_event(peer_address, BTIF_AV_AVRCP_CLOSE_EVT); |
| } |
| } |
| |
| void btif_av_avrcp_event_remote_play(const RawAddress& peer_address) { |
| // TODO: We need a better demultipexing mechanism whether the remote device |
| // is an A2DP Source or a Sink. |
| if (btif_av_source.Enabled()) { |
| btif_av_source_dispatch_sm_event(peer_address, |
| BTIF_AV_AVRCP_REMOTE_PLAY_EVT); |
| } else if (btif_av_sink.Enabled()) { |
| btif_av_sink_dispatch_sm_event(peer_address, BTIF_AV_AVRCP_REMOTE_PLAY_EVT); |
| } |
| } |
| |
| bool btif_av_is_peer_edr(const RawAddress& peer_address) { |
| BtifAvPeer* peer = btif_av_find_peer(peer_address); |
| if (peer == nullptr) { |
| BTIF_TRACE_WARNING("%s: No peer found for peer_address=%s", __func__, |
| peer_address.ToString().c_str()); |
| return false; |
| } |
| if (!peer->IsConnected()) { |
| BTIF_TRACE_WARNING("%s: Peer %s is not connected", __func__, |
| peer_address.ToString().c_str()); |
| return false; |
| } |
| |
| bool is_edr = peer->IsEdr(); |
| BTIF_TRACE_DEBUG("%s: Peer %s : is_edr=%d", __func__, |
| peer_address.ToString().c_str(), is_edr); |
| return is_edr; |
| } |
| |
| bool btif_av_peer_supports_3mbps(const RawAddress& peer_address) { |
| BtifAvPeer* peer = btif_av_find_peer(peer_address); |
| if (peer == nullptr) { |
| BTIF_TRACE_WARNING("%s: No peer found for peer_address=%s", __func__, |
| peer_address.ToString().c_str()); |
| return false; |
| } |
| |
| bool is3mbps = peer->Is3Mbps(); |
| bool is_connected = peer->IsConnected(); |
| BTIF_TRACE_DEBUG("%s: Peer %s : connected=%d, edr_3mbps=%d", __func__, |
| peer_address.ToString().c_str(), is_connected, is3mbps); |
| return (is_connected && is3mbps); |
| } |
| |
| void btif_av_acl_disconnected(const RawAddress& peer_address) { |
| // Inform the application that ACL is disconnected and move to idle state |
| BTIF_TRACE_WARNING("%s: Peer %s : ACL Disconnected", __func__, |
| peer_address.ToString().c_str()); |
| |
| if (btif_av_source.Enabled()) { |
| btif_av_source_dispatch_sm_event(peer_address, BTIF_AV_ACL_DISCONNECTED); |
| } else if (btif_av_sink.Enabled()) { |
| btif_av_sink_dispatch_sm_event(peer_address, BTIF_AV_ACL_DISCONNECTED); |
| } |
| } |
| |
| static void btif_debug_av_peer_dump(int fd, const BtifAvPeer& peer) { |
| std::string state_str; |
| int state = peer.StateMachine().StateId(); |
| switch (state) { |
| case BtifAvStateMachine::kStateIdle: |
| state_str = "Idle"; |
| break; |
| case BtifAvStateMachine::kStateOpening: |
| state_str = "Opening"; |
| break; |
| case BtifAvStateMachine::kStateOpened: |
| state_str = "Opened"; |
| break; |
| case BtifAvStateMachine::kStateStarted: |
| state_str = "Started"; |
| break; |
| case BtifAvStateMachine::kStateClosing: |
| state_str = "Closing"; |
| break; |
| default: |
| state_str = "Unknown(" + std::to_string(state) + ")"; |
| break; |
| } |
| |
| dprintf(fd, " Peer: %s\n", peer.PeerAddress().ToString().c_str()); |
| dprintf(fd, " Connected: %s\n", peer.IsConnected() ? "true" : "false"); |
| dprintf(fd, " Streaming: %s\n", peer.IsStreaming() ? "true" : "false"); |
| dprintf(fd, " SEP: %d(%s)\n", peer.PeerSep(), |
| (peer.IsSource()) ? "Source" : "Sink"); |
| dprintf(fd, " State Machine: %s\n", state_str.c_str()); |
| dprintf(fd, " Flags: %s\n", peer.FlagsToString().c_str()); |
| dprintf(fd, " OpenOnRcTimer: %s\n", |
| alarm_is_scheduled(peer.AvOpenOnRcTimer()) ? "Scheduled" |
| : "Not scheduled"); |
| dprintf(fd, " BTA Handle: %d\n", peer.BtaHandle()); |
| dprintf(fd, " Peer ID: %d\n", peer.PeerId()); |
| dprintf(fd, " EDR: %s\n", peer.IsEdr() ? "true" : "false"); |
| dprintf(fd, " Support 3Mbps: %s\n", peer.Is3Mbps() ? "true" : "false"); |
| dprintf(fd, " Self Initiated Connection: %s\n", |
| peer.SelfInitiatedConnection() ? "true" : "false"); |
| } |
| |
| static void btif_debug_av_source_dump(int fd) { |
| bool enabled = btif_av_source.Enabled(); |
| |
| dprintf(fd, "\nA2DP Source State: %s\n", (enabled) ? "Enabled" : "Disabled"); |
| if (!enabled) return; |
| dprintf(fd, " Active peer: %s\n", |
| btif_av_source.ActivePeer().ToString().c_str()); |
| for (auto it : btif_av_source.Peers()) { |
| const BtifAvPeer* peer = it.second; |
| btif_debug_av_peer_dump(fd, *peer); |
| } |
| } |
| |
| static void btif_debug_av_sink_dump(int fd) { |
| bool enabled = btif_av_sink.Enabled(); |
| |
| dprintf(fd, "\nA2DP Sink State: %s\n", (enabled) ? "Enabled" : "Disabled"); |
| if (!enabled) return; |
| dprintf(fd, " Active peer: %s\n", |
| btif_av_sink.ActivePeer().ToString().c_str()); |
| dprintf(fd, " Peers:\n"); |
| for (auto it : btif_av_sink.Peers()) { |
| const BtifAvPeer* peer = it.second; |
| btif_debug_av_peer_dump(fd, *peer); |
| } |
| } |
| |
| void btif_debug_av_dump(int fd) { |
| btif_debug_av_source_dump(fd); |
| btif_debug_av_sink_dump(fd); |
| } |