diff --git a/bluetooth/audio/2.2/IBluetoothAudioProvider.hal b/bluetooth/audio/2.2/IBluetoothAudioProvider.hal index bc16b01373..f57753709c 100644 --- a/bluetooth/audio/2.2/IBluetoothAudioProvider.hal +++ b/bluetooth/audio/2.2/IBluetoothAudioProvider.hal @@ -59,4 +59,14 @@ interface IBluetoothAudioProvider extends @2.1::IBluetoothAudioProvider { */ startSession_2_2(IBluetoothAudioPort hostIf, AudioConfiguration audioConfig) generates (Status status, fmq_sync dataMQ); + + /** + * Called when the audio configuration of the stream has been changed. + * + * @param audioConfig The audio configuration negotiated with the remote + * device. The PCM parameters are set if software based encoding, + * otherwise the correct codec configuration is used for hardware + * encoding. + */ + updateAudioConfiguration(AudioConfiguration audioConfig); }; diff --git a/bluetooth/audio/2.2/default/BluetoothAudioProvider.cpp b/bluetooth/audio/2.2/default/BluetoothAudioProvider.cpp index 18ac2925ad..202cfb9f90 100644 --- a/bluetooth/audio/2.2/default/BluetoothAudioProvider.cpp +++ b/bluetooth/audio/2.2/default/BluetoothAudioProvider.cpp @@ -186,6 +186,29 @@ Return BluetoothAudioProvider::endSession() { return Void(); } +Return BluetoothAudioProvider::updateAudioConfiguration( + const AudioConfiguration& audioConfig) { + LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_); + + if (stack_iface_ == nullptr) { + LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) + << " has NO session"; + return Void(); + } + + if (audioConfig.getDiscriminator() != audio_config_.getDiscriminator()) { + LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) + << " audio config type is not match"; + return Void(); + } + + audio_config_ = audioConfig; + BluetoothAudioSessionReport_2_2::ReportAudioConfigChanged(session_type_, + audio_config_); + + return Void(); +} + } // namespace implementation } // namespace V2_2 } // namespace audio diff --git a/bluetooth/audio/2.2/default/BluetoothAudioProvider.h b/bluetooth/audio/2.2/default/BluetoothAudioProvider.h index 0f1f3c6411..425ea3b303 100644 --- a/bluetooth/audio/2.2/default/BluetoothAudioProvider.h +++ b/bluetooth/audio/2.2/default/BluetoothAudioProvider.h @@ -53,6 +53,8 @@ class BluetoothAudioProvider : public IBluetoothAudioProvider { Return streamStarted(BluetoothAudioStatus status) override; Return streamSuspended(BluetoothAudioStatus status) override; Return endSession() override; + Return updateAudioConfiguration( + const AudioConfiguration& audioConfig) override; protected: sp death_recipient_; diff --git a/bluetooth/audio/utils/session/BluetoothAudioSessionControl_2_2.h b/bluetooth/audio/utils/session/BluetoothAudioSessionControl_2_2.h index 71ab4645a8..368939e6f8 100644 --- a/bluetooth/audio/utils/session/BluetoothAudioSessionControl_2_2.h +++ b/bluetooth/audio/utils/session/BluetoothAudioSessionControl_2_2.h @@ -48,20 +48,38 @@ class BluetoothAudioSessionControl_2_2 { std::shared_ptr session_ptr = BluetoothAudioSessionInstance_2_2::GetSessionInstance(session_type); if (session_ptr != nullptr) { - return session_ptr->GetAudioSession()->RegisterStatusCback(cbacks); + PortStatusCallbacks_2_2 cb = { + .control_result_cb_ = cbacks.control_result_cb_, + .session_changed_cb_ = cbacks.session_changed_cb_, + .audio_configuration_changed_cb_ = nullptr}; + return session_ptr->RegisterStatusCback(cb); + } + return kObserversCookieUndefined; + } + + // The control API helps the bluetooth_audio module to register + // PortStatusCallbacks_2_2 + // @return: cookie - the assigned number to this bluetooth_audio output + static uint16_t RegisterControlResultCback( + const SessionType_2_1& session_type, + const PortStatusCallbacks_2_2& cbacks) { + std::shared_ptr session_ptr = + BluetoothAudioSessionInstance_2_2::GetSessionInstance(session_type); + if (session_ptr != nullptr) { + return session_ptr->RegisterStatusCback(cbacks); } return kObserversCookieUndefined; } // The control API helps the bluetooth_audio module to unregister - // PortStatusCallbacks + // PortStatusCallbacks and PortStatusCallbacks_2_2 // @param: cookie - indicates which bluetooth_audio output is static void UnregisterControlResultCback(const SessionType_2_1& session_type, uint16_t cookie) { std::shared_ptr session_ptr = BluetoothAudioSessionInstance_2_2::GetSessionInstance(session_type); if (session_ptr != nullptr) { - session_ptr->GetAudioSession()->UnregisterStatusCback(cookie); + session_ptr->UnregisterStatusCback(cookie); } } diff --git a/bluetooth/audio/utils/session/BluetoothAudioSessionReport_2_2.h b/bluetooth/audio/utils/session/BluetoothAudioSessionReport_2_2.h index 79121cc8f9..17e140e60f 100644 --- a/bluetooth/audio/utils/session/BluetoothAudioSessionReport_2_2.h +++ b/bluetooth/audio/utils/session/BluetoothAudioSessionReport_2_2.h @@ -60,7 +60,20 @@ class BluetoothAudioSessionReport_2_2 { std::shared_ptr session_ptr = BluetoothAudioSessionInstance_2_2::GetSessionInstance(session_type); if (session_ptr != nullptr) { - session_ptr->GetAudioSession()->ReportControlStatus(start_resp, status); + session_ptr->ReportControlStatus(start_resp, status); + } + } + // The API reports the Bluetooth stack has replied the changed of the audio + // configuration, and will inform registered bluetooth_audio outputs + static void ReportAudioConfigChanged( + const ::android::hardware::bluetooth::audio::V2_1::SessionType& + session_type, + const ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration& + audio_config) { + std::shared_ptr session_ptr = + BluetoothAudioSessionInstance_2_2::GetSessionInstance(session_type); + if (session_ptr != nullptr) { + session_ptr->ReportAudioConfigChanged(audio_config); } } }; diff --git a/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.cpp b/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.cpp index db1619b5fc..60ac4ece6f 100644 --- a/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.cpp +++ b/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.cpp @@ -359,7 +359,7 @@ void BluetoothAudioSession_2_2::OnSessionStarted( audio_session->stack_iface_ = stack_iface; LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_2_1_) << ", AudioConfiguration=" << toString(audio_config); - audio_session->ReportSessionStatus(); + ReportSessionStatus(); }; } } @@ -386,7 +386,150 @@ void BluetoothAudioSession_2_2::OnSessionEnded() { audio_session->stack_iface_ = nullptr; audio_session->UpdateDataPath(nullptr); if (toggled) { + ReportSessionStatus(); + } +} + +// The control function helps the bluetooth_audio module to register +// PortStatusCallbacks_2_2 +// @return: cookie - the assigned number to this bluetooth_audio output +uint16_t BluetoothAudioSession_2_2::RegisterStatusCback( + const PortStatusCallbacks_2_2& cbacks) { + if (session_type_2_1_ != + SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH && + session_type_2_1_ != + SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH) { + PortStatusCallbacks cb = { + .control_result_cb_ = cbacks.control_result_cb_, + .session_changed_cb_ = cbacks.session_changed_cb_}; + return audio_session->RegisterStatusCback(cb); + } + + std::lock_guard guard(audio_session->mutex_); + uint16_t cookie = ObserversCookieGetInitValue(session_type_2_1_); + uint16_t cookie_upper_bound = ObserversCookieGetUpperBound(session_type_2_1_); + + while (cookie < cookie_upper_bound) { + if (observers_.find(cookie) == observers_.end()) { + break; + } + ++cookie; + } + if (cookie >= cookie_upper_bound) { + LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_2_1_) + << " has " << observers_.size() + << " observers already (No Resource)"; + return kObserversCookieUndefined; + } + std::shared_ptr cb = + std::make_shared(); + *cb = cbacks; + observers_[cookie] = cb; + return cookie; +} + +// The control function helps the bluetooth_audio module to unregister +// PortStatusCallbacks_2_2 +// @param: cookie - indicates which bluetooth_audio output is +void BluetoothAudioSession_2_2::UnregisterStatusCback(uint16_t cookie) { + std::lock_guard guard(audio_session->mutex_); + if (session_type_2_1_ != + SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH && + session_type_2_1_ != + SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH) { + audio_session->UnregisterStatusCback(cookie); + return; + } + if (observers_.erase(cookie) != 1) { + LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_2_1_) + << " no such provider=0x" + << android::base::StringPrintf("%04x", cookie); + } +} + +// invoking the registered session_changed_cb_ +void BluetoothAudioSession_2_2::ReportSessionStatus() { + // This is locked already by OnSessionStarted / OnSessionEnded + if (session_type_2_1_ != + SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH && + session_type_2_1_ != + SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH) { audio_session->ReportSessionStatus(); + return; + } + if (observers_.empty()) { + LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_2_1_) + << " has NO port state observer"; + return; + } + for (auto& observer : observers_) { + uint16_t cookie = observer.first; + std::shared_ptr cb = observer.second; + LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_2_1_) + << " notify to bluetooth_audio=0x" + << android::base::StringPrintf("%04x", cookie); + cb->session_changed_cb_(cookie); + } +} + +// The report function is used to report that the Bluetooth stack has notified +// the result of startStream or suspendStream, and will invoke +// control_result_cb_ to notify registered bluetooth_audio outputs +void BluetoothAudioSession_2_2::ReportControlStatus( + bool start_resp, const BluetoothAudioStatus& status) { + if (session_type_2_1_ != + SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH && + session_type_2_1_ != + SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH) { + audio_session->ReportControlStatus(start_resp, status); + return; + } + std::lock_guard guard(audio_session->mutex_); + if (observers_.empty()) { + LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_2_1_) + << " has NO port state observer"; + return; + } + for (auto& observer : observers_) { + uint16_t cookie = observer.first; + std::shared_ptr cb = observer.second; + LOG(INFO) << __func__ << " - status=" << toString(status) + << " for SessionType=" << toString(session_type_2_1_) + << ", bluetooth_audio=0x" + << android::base::StringPrintf("%04x", cookie) + << (start_resp ? " started" : " suspended"); + cb->control_result_cb_(cookie, start_resp, status); + } +} + +// The report function is used to report that the Bluetooth stack has notified +// the result of startStream or suspendStream, and will invoke +// control_result_cb_ to notify registered bluetooth_audio outputs +void BluetoothAudioSession_2_2::ReportAudioConfigChanged( + const ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration& + audio_config) { + if (session_type_2_1_ != + SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH && + session_type_2_1_ != + SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH) { + return; + } + std::lock_guard guard(audio_session->mutex_); + audio_config_2_2_ = audio_config; + if (observers_.empty()) { + LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_2_1_) + << " has NO port state observer"; + return; + } + for (auto& observer : observers_) { + uint16_t cookie = observer.first; + std::shared_ptr cb = observer.second; + LOG(INFO) << __func__ << " for SessionType=" << toString(session_type_2_1_) + << ", bluetooth_audio=0x" + << android::base::StringPrintf("%04x", cookie); + if (cb->audio_configuration_changed_cb_ != nullptr) { + cb->audio_configuration_changed_cb_(cookie); + } } } diff --git a/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.h b/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.h index 6ac0188bfd..3673fd8ccf 100644 --- a/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.h +++ b/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.h @@ -28,6 +28,40 @@ namespace android { namespace bluetooth { namespace audio { +inline uint16_t ObserversCookieGetInitValue( + const ::android::hardware::bluetooth::audio::V2_1::SessionType& + session_type) { + return (static_cast(session_type) << 8 & 0xff00); +} +inline uint16_t ObserversCookieGetUpperBound( + const ::android::hardware::bluetooth::audio::V2_1::SessionType& + session_type) { + return (static_cast(session_type) << 8 & 0xff00) + + kObserversCookieSize; +} + +struct PortStatusCallbacks_2_2 { + // control_result_cb_ - when the Bluetooth stack reports results of + // streamStarted or streamSuspended, the BluetoothAudioProvider will invoke + // this callback to report to the bluetooth_audio module. + // @param: cookie - indicates which bluetooth_audio output should handle + // @param: start_resp - this report is for startStream or not + // @param: status - the result of startStream + std::function + control_result_cb_; + // session_changed_cb_ - when the Bluetooth stack start / end session, the + // BluetoothAudioProvider will invoke this callback to notify to the + // bluetooth_audio module. + // @param: cookie - indicates which bluetooth_audio output should handle + std::function session_changed_cb_; + // audio_configuration_changed_cb_ - when the Bluetooth stack change the audio + // configuration, the BluetoothAudioProvider will invoke this callback to + // notify to the bluetooth_audio module. + // @param: cookie - indicates which bluetooth_audio output should handle + std::function audio_configuration_changed_cb_; +}; + class BluetoothAudioSession_2_2 { private: std::shared_ptr audio_session; @@ -50,6 +84,13 @@ class BluetoothAudioSession_2_2 { static ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration invalidLeOffloadAudioConfiguration; + // saving those registered bluetooth_audio's callbacks + std::unordered_map> + observers_; + + // invoking the registered session_changed_cb_ + void ReportSessionStatus(); + public: BluetoothAudioSession_2_2( const ::android::hardware::bluetooth::audio::V2_1::SessionType& @@ -82,6 +123,29 @@ class BluetoothAudioSession_2_2 { bool SuspendStream(); void StopStream(); + // The control function helps the bluetooth_audio module to register + // PortStatusCallbacks_2_2 + // @return: cookie - the assigned number to this bluetooth_audio output + uint16_t RegisterStatusCback(const PortStatusCallbacks_2_2& cbacks); + + // The control function helps the bluetooth_audio module to unregister + // PortStatusCallbacks_2_2 + // @param: cookie - indicates which bluetooth_audio output is + void UnregisterStatusCback(uint16_t cookie); + + // The report function is used to report that the Bluetooth stack has notified + // the result of startStream or suspendStream, and will invoke + // control_result_cb_ to notify registered bluetooth_audio outputs + void ReportControlStatus(bool start_resp, const BluetoothAudioStatus& status); + + // The report function is used to report that the Bluetooth stack has notified + // the audio configuration changed, and will invoke + // audio_configuration_changed_cb_ to notify registered bluetooth_audio + // outputs + void ReportAudioConfigChanged( + const ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration& + audio_config); + // The control function is for the bluetooth_audio module to get the current // AudioConfiguration const ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration