/* * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "BTAudioProviderSession_2_2" #include "BluetoothAudioSession_2_2.h" #include #include #include namespace android { namespace bluetooth { namespace audio { using ::android::hardware::audio::common::V5_0::AudioSource; using ::android::hardware::audio::common::V5_0::RecordTrackMetadata; using ::android::hardware::audio::common::V5_0::SinkMetadata; using SessionType_2_1 = ::android::hardware::bluetooth::audio::V2_1::SessionType; using SessionType_2_0 = ::android::hardware::bluetooth::audio::V2_0::SessionType; using AudioConfiguration_2_1 = ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration; ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration BluetoothAudioSession_2_2::invalidSoftwareAudioConfiguration = {}; ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration BluetoothAudioSession_2_2::invalidOffloadAudioConfiguration = {}; using IBluetoothAudioPort_2_2 = ::android::hardware::bluetooth::audio::V2_2::IBluetoothAudioPort; namespace { bool is_2_0_session_type( const ::android::hardware::bluetooth::audio::V2_1::SessionType& session_type) { if (session_type == SessionType_2_1::A2DP_SOFTWARE_ENCODING_DATAPATH || session_type == SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH || session_type == SessionType_2_1::HEARING_AID_SOFTWARE_ENCODING_DATAPATH) { return true; } else { return false; } } } // namespace BluetoothAudioSession_2_2::BluetoothAudioSession_2_2( const ::android::hardware::bluetooth::audio::V2_1::SessionType& session_type) : audio_session(BluetoothAudioSessionInstance::GetSessionInstance( static_cast(session_type))), audio_session_2_1( BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type)) { if (is_2_0_session_type(session_type)) { session_type_2_1_ = (SessionType_2_1::UNKNOWN); } else { session_type_2_1_ = (session_type); } } bool BluetoothAudioSession_2_2::IsSessionReady() { if (session_type_2_1_ != SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH) { return audio_session->IsSessionReady(); } std::lock_guard guard(audio_session->mutex_); return audio_session->stack_iface_ != nullptr; } std::shared_ptr BluetoothAudioSession_2_2::GetAudioSession() { return audio_session; } std::shared_ptr BluetoothAudioSession_2_2::GetAudioSession_2_1() { return audio_session_2_1; } void BluetoothAudioSession_2_2::UpdateSinkMetadata( const struct sink_metadata* sink_metadata) { std::lock_guard guard(audio_session->mutex_); if (!IsSessionReady()) { LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_2_1_) << " has NO session"; return; } ssize_t track_count = sink_metadata->track_count; LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_2_1_) << ", " << track_count << " track(s)"; if (session_type_2_1_ == SessionType_2_1::A2DP_SOFTWARE_ENCODING_DATAPATH || session_type_2_1_ == SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH) { return; } struct record_track_metadata* track = sink_metadata->tracks; SinkMetadata sinkMetadata; RecordTrackMetadata* halMetadata; sinkMetadata.tracks.resize(track_count); halMetadata = sinkMetadata.tracks.data(); while (track_count && track) { halMetadata->source = static_cast(track->source); halMetadata->gain = track->gain; // halMetadata->destination leave unspecified LOG(INFO) << __func__ << " - SessionType=" << toString(GetAudioSession()->session_type_) << ", source=" << track->source << ", dest_device=" << track->dest_device << ", gain=" << track->gain << ", dest_device_address=" << track->dest_device_address; --track_count; ++track; ++halMetadata; } /* This is called just for 2.2 sessions, so it's safe to do this casting*/ IBluetoothAudioPort_2_2* stack_iface_2_2_ = static_cast(audio_session->stack_iface_.get()); auto hal_retval = stack_iface_2_2_->updateSinkMetadata(sinkMetadata); if (!hal_retval.isOk()) { LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType=" << toString(session_type_2_1_) << " failed"; } } // The control function is for the bluetooth_audio module to get the current // AudioConfiguration const ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration BluetoothAudioSession_2_2::GetAudioConfig() { std::lock_guard guard(audio_session->mutex_); if (IsSessionReady()) { // If session is unknown it means it should be 2.0 type if (session_type_2_1_ != SessionType_2_1::UNKNOWN) { if (audio_config_2_2_ != invalidSoftwareAudioConfiguration) return audio_config_2_2_; ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration toConf; const AudioConfiguration_2_1 fromConf = GetAudioSession_2_1()->GetAudioConfig(); if (fromConf.getDiscriminator() == AudioConfiguration_2_1::hidl_discriminator::pcmConfig) { toConf.pcmConfig() = fromConf.pcmConfig(); return toConf; } } ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration toConf; const AudioConfiguration fromConf = GetAudioSession()->GetAudioConfig(); // pcmConfig only differs between 2.0 and 2.1 in AudioConfiguration if (fromConf.getDiscriminator() == AudioConfiguration::hidl_discriminator::codecConfig) { toConf.codecConfig() = fromConf.codecConfig(); } else { toConf.pcmConfig() = { .sampleRate = static_cast< ::android::hardware::bluetooth::audio::V2_1::SampleRate>( fromConf.pcmConfig().sampleRate), .channelMode = fromConf.pcmConfig().channelMode, .bitsPerSample = fromConf.pcmConfig().bitsPerSample, .dataIntervalUs = 0}; } return toConf; } else if (session_type_2_1_ == SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH) { return kInvalidOffloadAudioConfiguration; } else { return kInvalidSoftwareAudioConfiguration; } } bool BluetoothAudioSession_2_2::UpdateAudioConfig( const ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration& audio_config) { bool is_software_session = (session_type_2_1_ == SessionType_2_1::A2DP_SOFTWARE_ENCODING_DATAPATH || session_type_2_1_ == SessionType_2_1::HEARING_AID_SOFTWARE_ENCODING_DATAPATH || session_type_2_1_ == SessionType_2_1::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH || session_type_2_1_ == SessionType_2_1::LE_AUDIO_SOFTWARE_DECODED_DATAPATH); bool is_offload_a2dp_session = (session_type_2_1_ == SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH); bool is_offload_le_audio_session = (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); auto audio_config_discriminator = audio_config.getDiscriminator(); bool is_software_audio_config = (is_software_session && audio_config_discriminator == ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration:: hidl_discriminator::pcmConfig); bool is_a2dp_offload_audio_config = (is_offload_a2dp_session && audio_config_discriminator == ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration:: hidl_discriminator::codecConfig); bool is_le_audio_offload_audio_config = (is_offload_le_audio_session && audio_config_discriminator == ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration:: hidl_discriminator::leAudioConfig); if (!is_software_audio_config && !is_a2dp_offload_audio_config && !is_le_audio_offload_audio_config) { return false; } audio_config_2_2_ = audio_config; return true; } // The report function is used to report that the Bluetooth stack has started // this session without any failure, and will invoke session_changed_cb_ to // notify those registered bluetooth_audio outputs void BluetoothAudioSession_2_2::OnSessionStarted( const sp stack_iface, const DataMQ::Descriptor* dataMQ, const ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration& audio_config) { if (session_type_2_1_ == SessionType_2_1::UNKNOWN) { ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration config; if (audio_config.getDiscriminator() == ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration:: hidl_discriminator::codecConfig) { config.codecConfig(audio_config.codecConfig()); } else { auto& tmpPcm = audio_config.pcmConfig(); config.pcmConfig( ::android::hardware::bluetooth::audio::V2_0::PcmParameters{ .sampleRate = static_cast(tmpPcm.sampleRate), .channelMode = tmpPcm.channelMode, .bitsPerSample = tmpPcm.bitsPerSample /*dataIntervalUs is not passed to 2.0 */ }); } audio_session->OnSessionStarted(stack_iface, dataMQ, config); } else { std::lock_guard guard(audio_session->mutex_); if (stack_iface == nullptr) { LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_2_1_) << ", IBluetoothAudioPort Invalid"; } else if (!UpdateAudioConfig(audio_config)) { LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_2_1_) << ", AudioConfiguration=" << toString(audio_config) << " Invalid"; } else if (!audio_session->UpdateDataPath(dataMQ)) { LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_2_1_) << " DataMQ Invalid"; audio_config_2_2_ = (session_type_2_1_ == SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH ? kInvalidOffloadAudioConfiguration : kInvalidSoftwareAudioConfiguration); } else { audio_session->stack_iface_ = stack_iface; LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_2_1_) << ", AudioConfiguration=" << toString(audio_config); audio_session->ReportSessionStatus(); }; } } std::unique_ptr BluetoothAudioSessionInstance_2_2::instance_ptr = std::unique_ptr( new BluetoothAudioSessionInstance_2_2()); // API to fetch the session of A2DP / Hearing Aid std::shared_ptr BluetoothAudioSessionInstance_2_2::GetSessionInstance( const SessionType_2_1& session_type) { std::lock_guard guard(instance_ptr->mutex_); if (!instance_ptr->sessions_map_.empty()) { auto entry = instance_ptr->sessions_map_.find(session_type); if (entry != instance_ptr->sessions_map_.end()) { return entry->second; } } std::shared_ptr session_ptr = std::make_shared(session_type); instance_ptr->sessions_map_[session_type] = session_ptr; return session_ptr; } } // namespace audio } // namespace bluetooth } // namespace android