diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp index 6c4d7ac975..b8e1df809a 100644 --- a/audio/aidl/default/Module.cpp +++ b/audio/aidl/default/Module.cpp @@ -93,32 +93,6 @@ bool hasDynamicProfilesOnly(const std::vector& profiles) { return std::all_of(profiles.begin(), profiles.end(), isDynamicProfile); } -// Note: does not assign an ID to the config. -bool generateDefaultPortConfig(const AudioPort& port, AudioPortConfig* config) { - const bool allowDynamicConfig = port.ext.getTag() == AudioPortExt::device; - *config = {}; - config->portId = port.id; - for (const auto& profile : port.profiles) { - if (isDynamicProfile(profile)) continue; - config->format = profile.format; - config->channelMask = *profile.channelMasks.begin(); - config->sampleRate = Int{.value = *profile.sampleRates.begin()}; - config->flags = port.flags; - config->ext = port.ext; - return true; - } - if (allowDynamicConfig) { - config->format = AudioFormatDescription{}; - config->channelMask = AudioChannelLayout{}; - config->sampleRate = Int{.value = 0}; - config->flags = port.flags; - config->ext = port.ext; - return true; - } - LOG(ERROR) << __func__ << ": port " << port.id << " only has dynamic profiles"; - return false; -} - bool findAudioProfile(const AudioPort& port, const AudioFormatDescription& format, AudioProfile* profile) { if (auto profilesIt = @@ -204,10 +178,11 @@ ndk::ScopedAStatus Module::createStreamContext( } auto& configs = getConfig().portConfigs; auto portConfigIt = findById(configs, in_portConfigId); + const int32_t nominalLatencyMs = getNominalLatencyMs(*portConfigIt); // Since this is a private method, it is assumed that // validity of the portConfigId has already been checked. - const int32_t minimumStreamBufferSizeFrames = calculateBufferSizeFrames( - getNominalLatencyMs(*portConfigIt), portConfigIt->sampleRate.value().value); + const int32_t minimumStreamBufferSizeFrames = + calculateBufferSizeFrames(nominalLatencyMs, portConfigIt->sampleRate.value().value); if (in_bufferSizeFrames < minimumStreamBufferSizeFrames) { LOG(ERROR) << __func__ << ": insufficient buffer size " << in_bufferSizeFrames << ", must be at least " << minimumStreamBufferSizeFrames; @@ -241,7 +216,7 @@ ndk::ScopedAStatus Module::createStreamContext( std::make_unique(1, true /*configureEventFlagWord*/), std::make_unique(1, true /*configureEventFlagWord*/), portConfigIt->format.value(), portConfigIt->channelMask.value(), - portConfigIt->sampleRate.value().value, flags, getNominalLatencyMs(*portConfigIt), + portConfigIt->sampleRate.value().value, flags, nominalLatencyMs, portConfigIt->ext.get().handle, std::make_unique(frameSize * in_bufferSizeFrames), asyncCallback, outEventCallback, @@ -328,6 +303,29 @@ ndk::ScopedAStatus Module::findPortIdForNewStream(int32_t in_portConfigId, Audio return ndk::ScopedAStatus::ok(); } +bool Module::generateDefaultPortConfig(const AudioPort& port, AudioPortConfig* config) { + const bool allowDynamicConfig = port.ext.getTag() == AudioPortExt::device; + for (const auto& profile : port.profiles) { + if (isDynamicProfile(profile)) continue; + config->format = profile.format; + config->channelMask = *profile.channelMasks.begin(); + config->sampleRate = Int{.value = *profile.sampleRates.begin()}; + config->flags = port.flags; + config->ext = port.ext; + return true; + } + if (allowDynamicConfig) { + config->format = AudioFormatDescription{}; + config->channelMask = AudioChannelLayout{}; + config->sampleRate = Int{.value = 0}; + config->flags = port.flags; + config->ext = port.ext; + return true; + } + LOG(ERROR) << __func__ << ": port " << port.id << " only has dynamic profiles"; + return false; +} + void Module::populateConnectedProfiles() { Configuration& config = getConfig(); for (const AudioPort& port : config.ports) { @@ -617,10 +615,11 @@ ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdA std::vector routesToMixPorts = getAudioRoutesForAudioPortImpl(templateId); std::set routableMixPortIds = getRoutableAudioPortIds(templateId, &routesToMixPorts); + const int32_t nextPortId = getConfig().nextPortId++; if (!mDebug.simulateDeviceConnections) { // Even if the device port has static profiles, the HAL module might need to update // them, or abort the connection process. - RETURN_STATUS_IF_ERROR(populateConnectedDevicePort(&connectedPort)); + RETURN_STATUS_IF_ERROR(populateConnectedDevicePort(&connectedPort, nextPortId)); } else if (hasDynamicProfilesOnly(connectedPort.profiles)) { auto& connectedProfiles = getConfig().connectedProfiles; if (auto connectedProfilesIt = connectedProfiles.find(templateId); @@ -644,7 +643,7 @@ ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdA } } - connectedPort.id = getConfig().nextPortId++; + connectedPort.id = nextPortId; auto [connectedPortsIt, _] = mConnectedDevicePorts.insert(std::pair(connectedPort.id, std::set())); LOG(DEBUG) << __func__ << ": template port " << templateId << " external device connected, " @@ -1035,6 +1034,18 @@ ndk::ScopedAStatus Module::setAudioPatch(const AudioPatch& in_requested, AudioPa ndk::ScopedAStatus Module::setAudioPortConfig(const AudioPortConfig& in_requested, AudioPortConfig* out_suggested, bool* _aidl_return) { + auto generate = [this](const AudioPort& port, AudioPortConfig* config) { + return generateDefaultPortConfig(port, config); + }; + return setAudioPortConfigImpl(in_requested, generate, out_suggested, _aidl_return); +} + +ndk::ScopedAStatus Module::setAudioPortConfigImpl( + const AudioPortConfig& in_requested, + const std::function& + fillPortConfig, + AudioPortConfig* out_suggested, bool* applied) { LOG(DEBUG) << __func__ << ": requested " << in_requested.toString(); auto& configs = getConfig().portConfigs; auto existing = configs.end(); @@ -1063,7 +1074,8 @@ ndk::ScopedAStatus Module::setAudioPortConfig(const AudioPortConfig& in_requeste *out_suggested = *existing; } else { AudioPortConfig newConfig; - if (generateDefaultPortConfig(*portIt, &newConfig)) { + newConfig.portId = portIt->id; + if (fillPortConfig(*portIt, &newConfig)) { *out_suggested = newConfig; } else { LOG(ERROR) << __func__ << ": unable generate a default config for port " << portId; @@ -1168,17 +1180,17 @@ ndk::ScopedAStatus Module::setAudioPortConfig(const AudioPortConfig& in_requeste if (existing == configs.end() && requestedIsValid && requestedIsFullySpecified) { out_suggested->id = getConfig().nextPortId++; configs.push_back(*out_suggested); - *_aidl_return = true; + *applied = true; LOG(DEBUG) << __func__ << ": created new port config " << out_suggested->toString(); } else if (existing != configs.end() && requestedIsValid) { *existing = *out_suggested; - *_aidl_return = true; + *applied = true; LOG(DEBUG) << __func__ << ": updated port config " << out_suggested->toString(); } else { LOG(DEBUG) << __func__ << ": not applied; existing config ? " << (existing != configs.end()) << "; requested is valid? " << requestedIsValid << ", fully specified? " << requestedIsFullySpecified; - *_aidl_return = false; + *applied = false; } return ndk::ScopedAStatus::ok(); } @@ -1530,7 +1542,7 @@ bool Module::isMmapSupported() { return mIsMmapSupported.value(); } -ndk::ScopedAStatus Module::populateConnectedDevicePort(AudioPort* audioPort) { +ndk::ScopedAStatus Module::populateConnectedDevicePort(AudioPort* audioPort, int32_t) { if (audioPort->ext.getTag() != AudioPortExt::device) { LOG(ERROR) << __func__ << ": not a device port: " << audioPort->toString(); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); diff --git a/audio/aidl/default/alsa/ModuleAlsa.cpp b/audio/aidl/default/alsa/ModuleAlsa.cpp index 8512631268..9a2cce73cc 100644 --- a/audio/aidl/default/alsa/ModuleAlsa.cpp +++ b/audio/aidl/default/alsa/ModuleAlsa.cpp @@ -34,7 +34,7 @@ using aidl::android::media::audio::common::AudioProfile; namespace aidl::android::hardware::audio::core { -ndk::ScopedAStatus ModuleAlsa::populateConnectedDevicePort(AudioPort* audioPort) { +ndk::ScopedAStatus ModuleAlsa::populateConnectedDevicePort(AudioPort* audioPort, int32_t) { auto deviceProfile = alsa::getDeviceProfile(*audioPort); if (!deviceProfile.has_value()) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); diff --git a/audio/aidl/default/bluetooth/DevicePortProxy.cpp b/audio/aidl/default/bluetooth/DevicePortProxy.cpp index 1be0875a55..d772c20090 100644 --- a/audio/aidl/default/bluetooth/DevicePortProxy.cpp +++ b/audio/aidl/default/bluetooth/DevicePortProxy.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define LOG_TAG "AHAL_BluetoothPortProxy" +#define LOG_TAG "AHAL_BluetoothAudioPort" #include #include @@ -254,12 +254,7 @@ bool BluetoothAudioPortAidl::inUse() const { return (mCookie != ::aidl::android::hardware::bluetooth::audio::kObserversCookieUndefined); } -bool BluetoothAudioPortAidl::getPreferredDataIntervalUs(size_t* interval_us) const { - if (!interval_us) { - LOG(ERROR) << __func__ << ": bad input arg"; - return false; - } - +bool BluetoothAudioPortAidl::getPreferredDataIntervalUs(size_t& interval_us) const { if (!inUse()) { LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use"; return false; @@ -272,16 +267,11 @@ bool BluetoothAudioPortAidl::getPreferredDataIntervalUs(size_t* interval_us) con return false; } - *interval_us = hal_audio_cfg.get().dataIntervalUs; + interval_us = hal_audio_cfg.get().dataIntervalUs; return true; } -bool BluetoothAudioPortAidl::loadAudioConfig(PcmConfiguration* audio_cfg) const { - if (!audio_cfg) { - LOG(ERROR) << __func__ << ": bad input arg"; - return false; - } - +bool BluetoothAudioPortAidl::loadAudioConfig(PcmConfiguration& audio_cfg) { if (!inUse()) { LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use"; return false; @@ -293,15 +283,26 @@ bool BluetoothAudioPortAidl::loadAudioConfig(PcmConfiguration* audio_cfg) const LOG(ERROR) << __func__ << ": unsupported audio cfg tag"; return false; } - *audio_cfg = hal_audio_cfg.get(); + audio_cfg = hal_audio_cfg.get(); LOG(VERBOSE) << __func__ << debugMessage() << ", state*=" << getState() << ", PcmConfig=[" - << audio_cfg->toString() << "]"; - if (audio_cfg->channelMode == ChannelMode::UNKNOWN) { + << audio_cfg.toString() << "]"; + if (audio_cfg.channelMode == ChannelMode::UNKNOWN) { return false; } return true; } +bool BluetoothAudioPortAidlOut::loadAudioConfig(PcmConfiguration& audio_cfg) { + if (!BluetoothAudioPortAidl::loadAudioConfig(audio_cfg)) return false; + // WAR to support Mono / 16 bits per sample as the Bluetooth stack requires + if (audio_cfg.channelMode == ChannelMode::MONO && audio_cfg.bitsPerSample == 16) { + mIsStereoToMono = true; + audio_cfg.channelMode = ChannelMode::STEREO; + LOG(INFO) << __func__ << ": force channels = to be AUDIO_CHANNEL_OUT_STEREO"; + } + return true; +} + bool BluetoothAudioPortAidl::standby() { if (!inUse()) { LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use"; @@ -435,7 +436,7 @@ bool BluetoothAudioPortAidl::suspend() { retval = condWaitState(BluetoothStreamState::SUSPENDING); } else { LOG(ERROR) << __func__ << debugMessage() << ", state=" << getState() - << " Hal fails"; + << " failure to suspend stream"; } } } diff --git a/audio/aidl/default/bluetooth/ModuleBluetooth.cpp b/audio/aidl/default/bluetooth/ModuleBluetooth.cpp index 8a1cbbfe1c..9084b3044c 100644 --- a/audio/aidl/default/bluetooth/ModuleBluetooth.cpp +++ b/audio/aidl/default/bluetooth/ModuleBluetooth.cpp @@ -24,13 +24,25 @@ using aidl::android::hardware::audio::common::SinkMetadata; using aidl::android::hardware::audio::common::SourceMetadata; +using aidl::android::hardware::bluetooth::audio::ChannelMode; +using aidl::android::hardware::bluetooth::audio::PcmConfiguration; +using aidl::android::media::audio::common::AudioChannelLayout; +using aidl::android::media::audio::common::AudioConfigBase; using aidl::android::media::audio::common::AudioDeviceDescription; using aidl::android::media::audio::common::AudioDeviceType; +using aidl::android::media::audio::common::AudioFormatDescription; +using aidl::android::media::audio::common::AudioFormatType; +using aidl::android::media::audio::common::AudioIoFlags; using aidl::android::media::audio::common::AudioOffloadInfo; using aidl::android::media::audio::common::AudioPort; +using aidl::android::media::audio::common::AudioPortConfig; using aidl::android::media::audio::common::AudioPortExt; +using aidl::android::media::audio::common::AudioProfile; +using aidl::android::media::audio::common::Int; using aidl::android::media::audio::common::MicrophoneInfo; +using aidl::android::media::audio::common::PcmType; using android::bluetooth::audio::aidl::BluetoothAudioPortAidl; +using android::bluetooth::audio::aidl::BluetoothAudioPortAidlIn; using android::bluetooth::audio::aidl::BluetoothAudioPortAidlOut; // TODO(b/312265159) bluetooth audio should be in its own process @@ -39,6 +51,35 @@ extern "C" binder_status_t createIBluetoothAudioProviderFactory(); namespace aidl::android::hardware::audio::core { +namespace { + +PcmType pcmTypeFromBitsPerSample(int8_t bitsPerSample) { + if (bitsPerSample == 8) + return PcmType::UINT_8_BIT; + else if (bitsPerSample == 16) + return PcmType::INT_16_BIT; + else if (bitsPerSample == 24) + return PcmType::INT_24_BIT; + else if (bitsPerSample == 32) + return PcmType::INT_32_BIT; + ALOGE("Unsupported bitsPerSample: %d", bitsPerSample); + return PcmType::DEFAULT; +} + +AudioChannelLayout channelLayoutFromChannelMode(ChannelMode mode) { + if (mode == ChannelMode::MONO) { + return AudioChannelLayout::make( + AudioChannelLayout::LAYOUT_MONO); + } else if (mode == ChannelMode::STEREO || mode == ChannelMode::DUALMONO) { + return AudioChannelLayout::make( + AudioChannelLayout::LAYOUT_STEREO); + } + ALOGE("Unsupported channel mode: %s", toString(mode).c_str()); + return AudioChannelLayout{}; +} + +} // namespace + ModuleBluetooth::ModuleBluetooth(std::unique_ptr&& config) : Module(Type::BLUETOOTH, std::move(config)) { // TODO(b/312265159) bluetooth audio should be in its own process @@ -95,66 +136,130 @@ ndk::ScopedAStatus ModuleBluetooth::setMicMute(bool in_mute __unused) { return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } +ndk::ScopedAStatus ModuleBluetooth::setAudioPortConfig(const AudioPortConfig& in_requested, + AudioPortConfig* out_suggested, + bool* _aidl_return) { + auto fillConfig = [this](const AudioPort& port, AudioPortConfig* config) { + if (port.ext.getTag() == AudioPortExt::device) { + CachedProxy proxy; + auto status = findOrCreateProxy(port, proxy); + if (status.isOk()) { + const auto& pcmConfig = proxy.pcmConfig; + LOG(DEBUG) << "setAudioPortConfig: suggesting port config from " + << pcmConfig.toString(); + const auto pcmType = pcmTypeFromBitsPerSample(pcmConfig.bitsPerSample); + const auto channelMask = channelLayoutFromChannelMode(pcmConfig.channelMode); + if (pcmType != PcmType::DEFAULT && channelMask != AudioChannelLayout{}) { + config->format = + AudioFormatDescription{.type = AudioFormatType::PCM, .pcm = pcmType}; + config->channelMask = channelMask; + config->sampleRate = Int{.value = pcmConfig.sampleRateHz}; + config->flags = port.flags; + config->ext = port.ext; + return true; + } + } + } + return generateDefaultPortConfig(port, config); + }; + return Module::setAudioPortConfigImpl(in_requested, fillConfig, out_suggested, _aidl_return); +} + +ndk::ScopedAStatus ModuleBluetooth::checkAudioPatchEndpointsMatch( + const std::vector& sources, const std::vector& sinks) { + // Both sources and sinks must be non-empty, this is guaranteed by 'setAudioPatch'. + const bool isInput = sources[0]->ext.getTag() == AudioPortExt::device; + const int32_t devicePortId = isInput ? sources[0]->portId : sinks[0]->portId; + const auto proxyIt = mProxies.find(devicePortId); + if (proxyIt == mProxies.end()) return ndk::ScopedAStatus::ok(); + const auto& pcmConfig = proxyIt->second.pcmConfig; + const AudioPortConfig* mixPortConfig = isInput ? sinks[0] : sources[0]; + if (!StreamBluetooth::checkConfigParams( + pcmConfig, AudioConfigBase{.sampleRate = mixPortConfig->sampleRate->value, + .channelMask = *(mixPortConfig->channelMask), + .format = *(mixPortConfig->format)})) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); + } + if (int32_t handle = mixPortConfig->ext.get().handle; handle > 0) { + mConnections.insert(std::pair(handle, devicePortId)); + } + return ndk::ScopedAStatus::ok(); +} + +void ModuleBluetooth::onExternalDeviceConnectionChanged(const AudioPort& audioPort, + bool connected) { + if (!connected) mProxies.erase(audioPort.id); +} + ndk::ScopedAStatus ModuleBluetooth::createInputStream( StreamContext&& context, const SinkMetadata& sinkMetadata, const std::vector& microphones, std::shared_ptr* result) { + CachedProxy proxy; + RETURN_STATUS_IF_ERROR(fetchAndCheckProxy(context, proxy)); return createStreamInstance(result, std::move(context), sinkMetadata, - microphones, getBtProfileManagerHandles()); + microphones, getBtProfileManagerHandles(), + proxy.ptr, proxy.pcmConfig); } ndk::ScopedAStatus ModuleBluetooth::createOutputStream( StreamContext&& context, const SourceMetadata& sourceMetadata, const std::optional& offloadInfo, std::shared_ptr* result) { + CachedProxy proxy; + RETURN_STATUS_IF_ERROR(fetchAndCheckProxy(context, proxy)); return createStreamInstance(result, std::move(context), sourceMetadata, - offloadInfo, getBtProfileManagerHandles()); + offloadInfo, getBtProfileManagerHandles(), + proxy.ptr, proxy.pcmConfig); } -ndk::ScopedAStatus ModuleBluetooth::populateConnectedDevicePort(AudioPort* audioPort) { +ndk::ScopedAStatus ModuleBluetooth::populateConnectedDevicePort(AudioPort* audioPort, + int32_t nextPortId) { if (audioPort->ext.getTag() != AudioPortExt::device) { LOG(ERROR) << __func__ << ": not a device port: " << audioPort->toString(); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } + if (!::aidl::android::hardware::bluetooth::audio::BluetoothAudioSession::IsAidlAvailable()) { + LOG(ERROR) << __func__ << ": IBluetoothAudioProviderFactory AIDL service not available"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } const auto& devicePort = audioPort->ext.get(); const auto& description = devicePort.device.type; - // Since the configuration of the BT module is static, there is nothing to populate here. - // However, this method must return an error when the device can not be connected, - // this is determined by the status of BT profiles. + // This method must return an error when the device can not be connected. if (description.connection == AudioDeviceDescription::CONNECTION_BT_A2DP) { bool isA2dpEnabled = false; if (!!mBluetoothA2dp) { RETURN_STATUS_IF_ERROR((*mBluetoothA2dp).isEnabled(&isA2dpEnabled)); } LOG(DEBUG) << __func__ << ": isA2dpEnabled: " << isA2dpEnabled; - return isA2dpEnabled ? ndk::ScopedAStatus::ok() - : ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + if (!isA2dpEnabled) return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } else if (description.connection == AudioDeviceDescription::CONNECTION_BT_LE) { bool isLeEnabled = false; if (!!mBluetoothLe) { RETURN_STATUS_IF_ERROR((*mBluetoothLe).isEnabled(&isLeEnabled)); } LOG(DEBUG) << __func__ << ": isLeEnabled: " << isLeEnabled; - return isLeEnabled ? ndk::ScopedAStatus::ok() - : ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + if (!isLeEnabled) return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } else if (description.connection == AudioDeviceDescription::CONNECTION_WIRELESS && description.type == AudioDeviceType::OUT_HEARING_AID) { - // Hearing aids can use a number of profiles, thus the only way to check - // connectivity is to try to talk to the BT HAL. - if (!::aidl::android::hardware::bluetooth::audio::BluetoothAudioSession:: - IsAidlAvailable()) { - return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); - } - std::shared_ptr proxy = std::shared_ptr( - std::make_shared()); - if (proxy->registerPort(description)) { - LOG(DEBUG) << __func__ << ": registered hearing aid port"; - proxy->unregisterPort(); - return ndk::ScopedAStatus::ok(); - } - LOG(DEBUG) << __func__ << ": failed to register hearing aid port"; - return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + // Hearing aids can use a number of profiles, no single switch exists. + } else { + LOG(ERROR) << __func__ << ": unsupported device type: " << audioPort->toString(); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } - LOG(ERROR) << __func__ << ": unsupported device type: " << audioPort->toString(); - return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + CachedProxy proxy; + RETURN_STATUS_IF_ERROR(createProxy(*audioPort, nextPortId, proxy)); + // Since the device is already connected and configured by the BT stack, provide + // the current configuration instead of all possible profiles. + const auto& pcmConfig = proxy.pcmConfig; + audioPort->profiles.clear(); + audioPort->profiles.push_back( + AudioProfile{.format = AudioFormatDescription{.type = AudioFormatType::PCM, + .pcm = pcmTypeFromBitsPerSample( + pcmConfig.bitsPerSample)}, + .channelMasks = std::vector( + {channelLayoutFromChannelMode(pcmConfig.channelMode)}), + .sampleRates = std::vector({pcmConfig.sampleRateHz})}); + LOG(DEBUG) << __func__ << ": " << audioPort->toString(); + return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus ModuleBluetooth::onMasterMuteChanged(bool) { @@ -167,4 +272,77 @@ ndk::ScopedAStatus ModuleBluetooth::onMasterVolumeChanged(float) { return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } +int32_t ModuleBluetooth::getNominalLatencyMs(const AudioPortConfig& portConfig) { + const auto connectionsIt = mConnections.find(portConfig.ext.get().handle); + if (connectionsIt != mConnections.end()) { + const auto proxyIt = mProxies.find(connectionsIt->second); + if (proxyIt != mProxies.end()) { + auto proxy = proxyIt->second.ptr; + size_t dataIntervalUs = 0; + if (!proxy->getPreferredDataIntervalUs(dataIntervalUs)) { + LOG(WARNING) << __func__ << ": could not fetch preferred data interval"; + } + const bool isInput = portConfig.flags->getTag() == AudioIoFlags::input; + return isInput ? StreamInBluetooth::getNominalLatencyMs(dataIntervalUs) + : StreamOutBluetooth::getNominalLatencyMs(dataIntervalUs); + } + } + LOG(ERROR) << __func__ << ": no connection or proxy found for " << portConfig.toString(); + return Module::getNominalLatencyMs(portConfig); +} + +ndk::ScopedAStatus ModuleBluetooth::createProxy(const AudioPort& audioPort, int32_t instancePortId, + CachedProxy& proxy) { + const bool isInput = audioPort.flags.getTag() == AudioIoFlags::input; + proxy.ptr = isInput ? std::shared_ptr( + std::make_shared()) + : std::shared_ptr( + std::make_shared()); + const auto& devicePort = audioPort.ext.get(); + if (const auto device = devicePort.device.type; !proxy.ptr->registerPort(device)) { + LOG(ERROR) << __func__ << ": failed to register BT port for " << device.toString(); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + if (!proxy.ptr->loadAudioConfig(proxy.pcmConfig)) { + LOG(ERROR) << __func__ << ": state=" << proxy.ptr->getState() + << ", failed to load audio config"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + mProxies.insert(std::pair(instancePortId, proxy)); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus ModuleBluetooth::fetchAndCheckProxy(const StreamContext& context, + CachedProxy& proxy) { + const auto connectionsIt = mConnections.find(context.getMixPortHandle()); + if (connectionsIt != mConnections.end()) { + const auto proxyIt = mProxies.find(connectionsIt->second); + if (proxyIt != mProxies.end()) { + proxy = proxyIt->second; + mProxies.erase(proxyIt); + } + mConnections.erase(connectionsIt); + } + if (proxy.ptr != nullptr) { + if (!StreamBluetooth::checkConfigParams( + proxy.pcmConfig, AudioConfigBase{.sampleRate = context.getSampleRate(), + .channelMask = context.getChannelLayout(), + .format = context.getFormat()})) { + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + } + // Not having a proxy is OK, it may happen in VTS tests when streams are opened on unconnected + // mix ports. + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus ModuleBluetooth::findOrCreateProxy(const AudioPort& audioPort, + CachedProxy& proxy) { + if (auto proxyIt = mProxies.find(audioPort.id); proxyIt != mProxies.end()) { + proxy = proxyIt->second; + return ndk::ScopedAStatus::ok(); + } + return createProxy(audioPort, audioPort.id, proxy); +} + } // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/bluetooth/StreamBluetooth.cpp b/audio/aidl/default/bluetooth/StreamBluetooth.cpp index 0cee7f4002..a73af1b6b4 100644 --- a/audio/aidl/default/bluetooth/StreamBluetooth.cpp +++ b/audio/aidl/default/bluetooth/StreamBluetooth.cpp @@ -14,8 +14,9 @@ * limitations under the License. */ -#define LOG_TAG "AHAL_StreamBluetooth" +#include +#define LOG_TAG "AHAL_StreamBluetooth" #include #include #include @@ -31,6 +32,7 @@ using aidl::android::hardware::bluetooth::audio::ChannelMode; using aidl::android::hardware::bluetooth::audio::PcmConfiguration; using aidl::android::hardware::bluetooth::audio::PresentationPosition; using aidl::android::media::audio::common::AudioChannelLayout; +using aidl::android::media::audio::common::AudioConfigBase; using aidl::android::media::audio::common::AudioDevice; using aidl::android::media::audio::common::AudioDeviceAddress; using aidl::android::media::audio::common::AudioFormatDescription; @@ -48,51 +50,33 @@ namespace aidl::android::hardware::audio::core { constexpr int kBluetoothDefaultInputBufferMs = 20; constexpr int kBluetoothDefaultOutputBufferMs = 10; // constexpr int kBluetoothSpatializerOutputBufferMs = 10; +constexpr int kBluetoothDefaultRemoteDelayMs = 200; -// pcm configuration params are not really used by the module StreamBluetooth::StreamBluetooth(StreamContext* context, const Metadata& metadata, - ModuleBluetooth::BtProfileHandles&& btHandles) + ModuleBluetooth::BtProfileHandles&& btHandles, + const std::shared_ptr& btDeviceProxy, + const PcmConfiguration& pcmConfig) : StreamCommonImpl(context, metadata), - mSampleRate(getContext().getSampleRate()), - mChannelLayout(getContext().getChannelLayout()), - mFormat(getContext().getFormat()), mFrameSizeBytes(getContext().getFrameSize()), mIsInput(isInput(metadata)), mBluetoothA2dp(std::move(std::get(btHandles))), - mBluetoothLe(std::move(std::get(btHandles))) { - mPreferredDataIntervalUs = - (mIsInput ? kBluetoothDefaultInputBufferMs : kBluetoothDefaultOutputBufferMs) * 1000; - mPreferredFrameCount = frameCountFromDurationUs(mPreferredDataIntervalUs, mSampleRate); - mIsInitialized = false; - mIsReadyToClose = false; -} + mBluetoothLe(std::move(std::get(btHandles))), + mPreferredDataIntervalUs(pcmConfig.dataIntervalUs != 0 + ? pcmConfig.dataIntervalUs + : (mIsInput ? kBluetoothDefaultInputBufferMs + : kBluetoothDefaultOutputBufferMs) * + 1000), + mPreferredFrameCount( + frameCountFromDurationUs(mPreferredDataIntervalUs, pcmConfig.sampleRateHz)), + mBtDeviceProxy(btDeviceProxy) {} ::android::status_t StreamBluetooth::init() { - return ::android::OK; // defering this till we get AudioDeviceDescription -} - -const StreamCommonInterface::ConnectedDevices& StreamBluetooth::getConnectedDevices() const { std::lock_guard guard(mLock); - return StreamCommonImpl::getConnectedDevices(); -} - -ndk::ScopedAStatus StreamBluetooth::setConnectedDevices( - const std::vector& connectedDevices) { - if (mIsInput && connectedDevices.size() > 1) { - LOG(ERROR) << __func__ << ": wrong device size(" << connectedDevices.size() - << ") for input stream"; - return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); + if (mBtDeviceProxy == nullptr) { + // This is a normal situation in VTS tests. + LOG(INFO) << __func__ << ": no BT HAL proxy, stream is non-functional"; } - for (const auto& connectedDevice : connectedDevices) { - if (connectedDevice.address.getTag() != AudioDeviceAddress::mac) { - LOG(ERROR) << __func__ << ": bad device address" << connectedDevice.address.toString(); - return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); - } - } - std::lock_guard guard(mLock); - RETURN_STATUS_IF_ERROR(StreamCommonImpl::setConnectedDevices(connectedDevices)); - mIsInitialized = false; // updated connected device list, need initialization - return ndk::ScopedAStatus::ok(); + return ::android::OK; } ::android::status_t StreamBluetooth::drain(StreamDescriptor::DrainMode) { @@ -112,167 +96,111 @@ ndk::ScopedAStatus StreamBluetooth::setConnectedDevices( ::android::status_t StreamBluetooth::transfer(void* buffer, size_t frameCount, size_t* actualFrameCount, int32_t* latencyMs) { std::lock_guard guard(mLock); - if (!mIsInitialized || mIsReadyToClose) { - // 'setConnectedDevices' has been called or stream is ready to close, so no transfers + if (mBtDeviceProxy == nullptr || mBtDeviceProxy->getState() == BluetoothStreamState::DISABLED) { *actualFrameCount = 0; *latencyMs = StreamDescriptor::LATENCY_UNKNOWN; return ::android::OK; } *actualFrameCount = 0; *latencyMs = 0; - for (auto proxy : mBtDeviceProxies) { - if (!proxy->start()) { - LOG(ERROR) << __func__ << ": state = " << proxy->getState() << " failed to start "; - return -EIO; - } - const size_t fc = std::min(frameCount, mPreferredFrameCount); - const size_t bytesToTransfer = fc * mFrameSizeBytes; - if (mIsInput) { - const size_t totalRead = proxy->readData(buffer, bytesToTransfer); - *actualFrameCount = std::max(*actualFrameCount, totalRead / mFrameSizeBytes); - } else { - const size_t totalWrite = proxy->writeData(buffer, bytesToTransfer); - *actualFrameCount = std::max(*actualFrameCount, totalWrite / mFrameSizeBytes); - } - PresentationPosition presentation_position; - if (!proxy->getPresentationPosition(presentation_position)) { - LOG(ERROR) << __func__ << ": getPresentationPosition returned error "; - return ::android::UNKNOWN_ERROR; - } - *latencyMs = - std::max(*latencyMs, (int32_t)(presentation_position.remoteDeviceAudioDelayNanos / - NANOS_PER_MILLISECOND)); + if (!mBtDeviceProxy->start()) { + LOG(ERROR) << __func__ << ": state= " << mBtDeviceProxy->getState() << " failed to start"; + return -EIO; } + const size_t fc = std::min(frameCount, mPreferredFrameCount); + const size_t bytesToTransfer = fc * mFrameSizeBytes; + if (mIsInput) { + const size_t totalRead = mBtDeviceProxy->readData(buffer, bytesToTransfer); + *actualFrameCount = std::max(*actualFrameCount, totalRead / mFrameSizeBytes); + } else { + const size_t totalWrite = mBtDeviceProxy->writeData(buffer, bytesToTransfer); + *actualFrameCount = std::max(*actualFrameCount, totalWrite / mFrameSizeBytes); + } + PresentationPosition presentation_position; + if (!mBtDeviceProxy->getPresentationPosition(presentation_position)) { + presentation_position.remoteDeviceAudioDelayNanos = + kBluetoothDefaultRemoteDelayMs * NANOS_PER_MILLISECOND; + LOG(WARNING) << __func__ << ": getPresentationPosition failed, latency info is unavailable"; + } + // TODO(b/317117580): incorporate logic from + // packages/modules/Bluetooth/system/audio_bluetooth_hw/stream_apis.cc + // out_calculate_feeding_delay_ms / in_calculate_starving_delay_ms + *latencyMs = std::max(*latencyMs, (int32_t)(presentation_position.remoteDeviceAudioDelayNanos / + NANOS_PER_MILLISECOND)); return ::android::OK; } -::android::status_t StreamBluetooth::initialize() { - if (!::aidl::android::hardware::bluetooth::audio::BluetoothAudioSession::IsAidlAvailable()) { - LOG(ERROR) << __func__ << ": IBluetoothAudioProviderFactory service not available"; - return ::android::UNKNOWN_ERROR; - } - if (StreamCommonImpl::getConnectedDevices().empty()) { - LOG(ERROR) << __func__ << ", has no connected devices"; - return ::android::NO_INIT; - } - // unregister older proxies (if any) - for (auto proxy : mBtDeviceProxies) { - proxy->stop(); - proxy->unregisterPort(); - } - mBtDeviceProxies.clear(); - for (auto it = StreamCommonImpl::getConnectedDevices().begin(); - it != StreamCommonImpl::getConnectedDevices().end(); ++it) { - std::shared_ptr proxy = - mIsInput ? std::shared_ptr( - std::make_shared()) - : std::shared_ptr( - std::make_shared()); - if (proxy->registerPort(it->type)) { - LOG(ERROR) << __func__ << ": cannot init HAL"; - return ::android::UNKNOWN_ERROR; - } - PcmConfiguration config; - if (!proxy->loadAudioConfig(&config)) { - LOG(ERROR) << __func__ << ": state=" << proxy->getState() - << " failed to get audio config"; - return ::android::UNKNOWN_ERROR; - } - // TODO: Ensure minimum duration for spatialized output? - // WAR to support Mono / 16 bits per sample as the Bluetooth stack required - if (!mIsInput && config.channelMode == ChannelMode::MONO && config.bitsPerSample == 16) { - proxy->forcePcmStereoToMono(true); - config.channelMode = ChannelMode::STEREO; - LOG(INFO) << __func__ << ": force channels = to be AUDIO_CHANNEL_OUT_STEREO"; - } - if (!checkConfigParams(config)) { - LOG(ERROR) << __func__ << " checkConfigParams failed"; - return ::android::UNKNOWN_ERROR; - } - mBtDeviceProxies.push_back(std::move(proxy)); - } - mIsInitialized = true; - return ::android::OK; -} - -bool StreamBluetooth::checkConfigParams( - ::aidl::android::hardware::bluetooth::audio::PcmConfiguration& config) { - if ((int)mSampleRate != config.sampleRateHz) { - LOG(ERROR) << __func__ << ": Sample Rate mismatch, stream val = " << mSampleRate - << " hal val = " << config.sampleRateHz; +// static +bool StreamBluetooth::checkConfigParams(const PcmConfiguration& pcmConfig, + const AudioConfigBase& config) { + if ((int)config.sampleRate != pcmConfig.sampleRateHz) { + LOG(ERROR) << __func__ << ": sample rate mismatch, stream value=" << config.sampleRate + << ", BT HAL value=" << pcmConfig.sampleRateHz; return false; } - auto channelCount = aidl::android::hardware::audio::common::getChannelCount(mChannelLayout); - if ((config.channelMode == ChannelMode::MONO && channelCount != 1) || - (config.channelMode == ChannelMode::STEREO && channelCount != 2)) { - LOG(ERROR) << __func__ << ": Channel count mismatch, stream val = " << channelCount - << " hal val = " << toString(config.channelMode); + const auto channelCount = + aidl::android::hardware::audio::common::getChannelCount(config.channelMask); + if ((pcmConfig.channelMode == ChannelMode::MONO && channelCount != 1) || + (pcmConfig.channelMode == ChannelMode::STEREO && channelCount != 2)) { + LOG(ERROR) << __func__ << ": Channel count mismatch, stream value=" << channelCount + << ", BT HAL value=" << toString(pcmConfig.channelMode); return false; } - if (mFormat.type != AudioFormatType::PCM) { - LOG(ERROR) << __func__ << ": unexpected format type " - << aidl::android::media::audio::common::toString(mFormat.type); + if (config.format.type != AudioFormatType::PCM) { + LOG(ERROR) << __func__ + << ": unexpected stream format type: " << toString(config.format.type); return false; } - int8_t bps = aidl::android::hardware::audio::common::getPcmSampleSizeInBytes(mFormat.pcm) * 8; - if (bps != config.bitsPerSample) { - LOG(ERROR) << __func__ << ": bits per sample mismatch, stream val = " << bps - << " hal val = " << config.bitsPerSample; + const int8_t bitsPerSample = + aidl::android::hardware::audio::common::getPcmSampleSizeInBytes(config.format.pcm) * 8; + if (bitsPerSample != pcmConfig.bitsPerSample) { + LOG(ERROR) << __func__ << ": bits per sample mismatch, stream value=" << bitsPerSample + << ", BT HAL value=" << pcmConfig.bitsPerSample; return false; } - if (config.dataIntervalUs > 0) { - mPreferredDataIntervalUs = - std::min((int32_t)mPreferredDataIntervalUs, config.dataIntervalUs); - mPreferredFrameCount = frameCountFromDurationUs(mPreferredDataIntervalUs, mSampleRate); - } return true; } ndk::ScopedAStatus StreamBluetooth::prepareToClose() { std::lock_guard guard(mLock); - mIsReadyToClose = true; + if (mBtDeviceProxy != nullptr) { + if (mBtDeviceProxy->getState() != BluetoothStreamState::DISABLED) { + mBtDeviceProxy->stop(); + } + } return ndk::ScopedAStatus::ok(); } ::android::status_t StreamBluetooth::standby() { std::lock_guard guard(mLock); - if (!mIsInitialized) { - if (auto status = initialize(); status != ::android::OK) return status; - } - for (auto proxy : mBtDeviceProxies) { - if (!proxy->suspend()) { - LOG(ERROR) << __func__ << ": state = " << proxy->getState() << " failed to stand by "; - return -EIO; - } - } + if (mBtDeviceProxy != nullptr) mBtDeviceProxy->suspend(); return ::android::OK; } ::android::status_t StreamBluetooth::start() { std::lock_guard guard(mLock); - if (!mIsInitialized) return initialize(); + if (mBtDeviceProxy != nullptr) mBtDeviceProxy->start(); return ::android::OK; } void StreamBluetooth::shutdown() { std::lock_guard guard(mLock); - for (auto proxy : mBtDeviceProxies) { - proxy->stop(); - proxy->unregisterPort(); + if (mBtDeviceProxy != nullptr) { + mBtDeviceProxy->stop(); + mBtDeviceProxy = nullptr; } - mBtDeviceProxies.clear(); } ndk::ScopedAStatus StreamBluetooth::updateMetadataCommon(const Metadata& metadata) { std::lock_guard guard(mLock); - if (!mIsInitialized) return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); + if (mBtDeviceProxy == nullptr) { + return ndk::ScopedAStatus::ok(); + } bool isOk = true; if (isInput(metadata)) { - isOk = mBtDeviceProxies[0]->updateSinkMetadata(std::get(metadata)); + isOk = mBtDeviceProxy->updateSinkMetadata(std::get(metadata)); } else { - for (auto proxy : mBtDeviceProxies) { - if (!proxy->updateSourceMetadata(std::get(metadata))) isOk = false; - } + isOk = mBtDeviceProxy->updateSourceMetadata(std::get(metadata)); } return isOk ? ndk::ScopedAStatus::ok() : ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); @@ -280,7 +208,6 @@ ndk::ScopedAStatus StreamBluetooth::updateMetadataCommon(const Metadata& metadat ndk::ScopedAStatus StreamBluetooth::bluetoothParametersUpdated() { if (mIsInput) { - LOG(WARNING) << __func__ << ": not handled"; return ndk::ScopedAStatus::ok(); } auto applyParam = [](const std::shared_ptr& proxy, @@ -297,15 +224,10 @@ ndk::ScopedAStatus StreamBluetooth::bluetoothParametersUpdated() { bool hasLeParam, enableLe; auto btLe = mBluetoothLe.lock(); hasLeParam = btLe != nullptr && btLe->isEnabled(&enableLe).isOk(); - std::unique_lock lock(mLock); - ::android::base::ScopedLockAssertion lock_assertion(mLock); - if (!mIsInitialized) { - LOG(WARNING) << __func__ << ": init not done"; - return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); - } - for (auto proxy : mBtDeviceProxies) { - if ((hasA2dpParam && proxy->isA2dp() && !applyParam(proxy, enableA2dp)) || - (hasLeParam && proxy->isLeAudio() && !applyParam(proxy, enableLe))) { + std::lock_guard guard(mLock); + if (mBtDeviceProxy != nullptr) { + if ((hasA2dpParam && mBtDeviceProxy->isA2dp() && !applyParam(mBtDeviceProxy, enableA2dp)) || + (hasLeParam && mBtDeviceProxy->isLeAudio() && !applyParam(mBtDeviceProxy, enableLe))) { LOG(DEBUG) << __func__ << ": applyParam failed"; return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } @@ -313,11 +235,20 @@ ndk::ScopedAStatus StreamBluetooth::bluetoothParametersUpdated() { return ndk::ScopedAStatus::ok(); } +// static +int32_t StreamInBluetooth::getNominalLatencyMs(size_t dataIntervalUs) { + if (dataIntervalUs == 0) dataIntervalUs = kBluetoothDefaultInputBufferMs * 1000LL; + return dataIntervalUs / 1000LL; +} + StreamInBluetooth::StreamInBluetooth(StreamContext&& context, const SinkMetadata& sinkMetadata, const std::vector& microphones, - ModuleBluetooth::BtProfileHandles&& btProfileHandles) + ModuleBluetooth::BtProfileHandles&& btProfileHandles, + const std::shared_ptr& btDeviceProxy, + const PcmConfiguration& pcmConfig) : StreamIn(std::move(context), microphones), - StreamBluetooth(&mContextInstance, sinkMetadata, std::move(btProfileHandles)) {} + StreamBluetooth(&mContextInstance, sinkMetadata, std::move(btProfileHandles), btDeviceProxy, + pcmConfig) {} ndk::ScopedAStatus StreamInBluetooth::getActiveMicrophones( std::vector* _aidl_return __unused) { @@ -325,11 +256,20 @@ ndk::ScopedAStatus StreamInBluetooth::getActiveMicrophones( return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } +// static +int32_t StreamOutBluetooth::getNominalLatencyMs(size_t dataIntervalUs) { + if (dataIntervalUs == 0) dataIntervalUs = kBluetoothDefaultOutputBufferMs * 1000LL; + return dataIntervalUs / 1000LL; +} + StreamOutBluetooth::StreamOutBluetooth(StreamContext&& context, const SourceMetadata& sourceMetadata, const std::optional& offloadInfo, - ModuleBluetooth::BtProfileHandles&& btProfileHandles) + ModuleBluetooth::BtProfileHandles&& btProfileHandles, + const std::shared_ptr& btDeviceProxy, + const PcmConfiguration& pcmConfig) : StreamOut(std::move(context), offloadInfo), - StreamBluetooth(&mContextInstance, sourceMetadata, std::move(btProfileHandles)) {} + StreamBluetooth(&mContextInstance, sourceMetadata, std::move(btProfileHandles), btDeviceProxy, + pcmConfig) {} } // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/include/core-impl/DevicePortProxy.h b/audio/aidl/default/include/core-impl/DevicePortProxy.h index 17a8cf3d9a..ccb23bbe5b 100644 --- a/audio/aidl/default/include/core-impl/DevicePortProxy.h +++ b/audio/aidl/default/include/core-impl/DevicePortProxy.h @@ -73,12 +73,7 @@ class BluetoothAudioPort { * Bluetooth stack */ virtual bool loadAudioConfig( - ::aidl::android::hardware::bluetooth::audio::PcmConfiguration*) const = 0; - - /** - * WAR to support Mono mode / 16 bits per sample - */ - virtual void forcePcmStereoToMono(bool) = 0; + ::aidl::android::hardware::bluetooth::audio::PcmConfiguration&) = 0; /** * When the Audio framework / HAL wants to change the stream state, it invokes @@ -145,7 +140,7 @@ class BluetoothAudioPort { virtual bool isLeAudio() const = 0; - virtual bool getPreferredDataIntervalUs(size_t*) const = 0; + virtual bool getPreferredDataIntervalUs(size_t&) const = 0; virtual size_t writeData(const void*, size_t) const { return 0; } @@ -162,10 +157,8 @@ class BluetoothAudioPortAidl : public BluetoothAudioPort { void unregisterPort() override; - bool loadAudioConfig(::aidl::android::hardware::bluetooth::audio::PcmConfiguration* audio_cfg) - const override; - - void forcePcmStereoToMono(bool force) override { mIsStereoToMono = force; } + bool loadAudioConfig( + ::aidl::android::hardware::bluetooth::audio::PcmConfiguration& audio_cfg) override; bool standby() override; bool start() override; @@ -193,7 +186,7 @@ class BluetoothAudioPortAidl : public BluetoothAudioPort { bool isLeAudio() const override; - bool getPreferredDataIntervalUs(size_t* interval_us) const override; + bool getPreferredDataIntervalUs(size_t& interval_us) const override; protected: uint16_t mCookie; @@ -228,6 +221,9 @@ class BluetoothAudioPortAidl : public BluetoothAudioPort { class BluetoothAudioPortAidlOut : public BluetoothAudioPortAidl { public: + bool loadAudioConfig( + ::aidl::android::hardware::bluetooth::audio::PcmConfiguration& audio_cfg) override; + // The audio data path to the Bluetooth stack (Software encoding) size_t writeData(const void* buffer, size_t bytes) const override; }; diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h index 572b5979cc..ce71d70903 100644 --- a/audio/aidl/default/include/core-impl/Module.h +++ b/audio/aidl/default/include/core-impl/Module.h @@ -16,6 +16,7 @@ #pragma once +#include #include #include #include @@ -188,7 +189,7 @@ class Module : public BnModule { // If the module is unable to populate the connected device port correctly, the returned error // code must correspond to the errors of `IModule.connectedExternalDevice` method. virtual ndk::ScopedAStatus populateConnectedDevicePort( - ::aidl::android::media::audio::common::AudioPort* audioPort); + ::aidl::android::media::audio::common::AudioPort* audioPort, int32_t nextPortId); // If the module finds that the patch endpoints configurations are not matched, the returned // error code must correspond to the errors of `IModule.setAudioPatch` method. virtual ndk::ScopedAStatus checkAudioPatchEndpointsMatch( @@ -210,6 +211,7 @@ class Module : public BnModule { const int32_t rawSizeFrames = aidl::android::hardware::audio::common::frameCountFromDurationMs(latencyMs, sampleRateHz); + if (latencyMs >= 5) return rawSizeFrames; int32_t powerOf2 = 1; while (powerOf2 < rawSizeFrames) powerOf2 <<= 1; return powerOf2; @@ -227,12 +229,16 @@ class Module : public BnModule { std::set findConnectedPortConfigIds(int32_t portConfigId); ndk::ScopedAStatus findPortIdForNewStream( int32_t in_portConfigId, ::aidl::android::media::audio::common::AudioPort** port); + // Note: does not assign an ID to the config. + bool generateDefaultPortConfig(const ::aidl::android::media::audio::common::AudioPort& port, + ::aidl::android::media::audio::common::AudioPortConfig* config); std::vector getAudioRoutesForAudioPortImpl(int32_t portId); Configuration& getConfig(); const ConnectedDevicePorts& getConnectedDevicePorts() const { return mConnectedDevicePorts; } bool getMasterMute() const { return mMasterMute; } bool getMasterVolume() const { return mMasterVolume; } bool getMicMute() const { return mMicMute; } + const ModuleDebug& getModuleDebug() const { return mDebug; } const Patches& getPatches() const { return mPatches; } std::set getRoutableAudioPortIds(int32_t portId, std::vector* routes = nullptr); @@ -243,6 +249,12 @@ class Module : public BnModule { template std::set portIdsFromPortConfigIds(C portConfigIds); void registerPatch(const AudioPatch& patch); + ndk::ScopedAStatus setAudioPortConfigImpl( + const ::aidl::android::media::audio::common::AudioPortConfig& in_requested, + const std::function& fillPortConfig, + ::aidl::android::media::audio::common::AudioPortConfig* out_suggested, bool* applied); ndk::ScopedAStatus updateStreamsConnectedState(const AudioPatch& oldPatch, const AudioPatch& newPatch); }; diff --git a/audio/aidl/default/include/core-impl/ModuleAlsa.h b/audio/aidl/default/include/core-impl/ModuleAlsa.h index 2774fe5b9e..3392b41268 100644 --- a/audio/aidl/default/include/core-impl/ModuleAlsa.h +++ b/audio/aidl/default/include/core-impl/ModuleAlsa.h @@ -33,7 +33,8 @@ class ModuleAlsa : public Module { protected: // Extension methods of 'Module'. ndk::ScopedAStatus populateConnectedDevicePort( - ::aidl::android::media::audio::common::AudioPort* audioPort) override; + ::aidl::android::media::audio::common::AudioPort* audioPort, + int32_t nextPortId) override; }; } // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/include/core-impl/ModuleBluetooth.h b/audio/aidl/default/include/core-impl/ModuleBluetooth.h index 631b08854c..9451411f48 100644 --- a/audio/aidl/default/include/core-impl/ModuleBluetooth.h +++ b/audio/aidl/default/include/core-impl/ModuleBluetooth.h @@ -16,7 +16,10 @@ #pragma once +#include + #include "core-impl/Bluetooth.h" +#include "core-impl/DevicePortProxy.h" #include "core-impl/Module.h" namespace aidl::android::hardware::audio::core { @@ -31,6 +34,11 @@ class ModuleBluetooth final : public Module { ModuleBluetooth(std::unique_ptr&& config); private: + struct CachedProxy { + std::shared_ptr<::android::bluetooth::audio::aidl::BluetoothAudioPortAidl> ptr; + ::aidl::android::hardware::bluetooth::audio::PcmConfiguration pcmConfig; + }; + ChildInterface& getBtA2dp(); ChildInterface& getBtLe(); BtProfileHandles getBtProfileManagerHandles(); @@ -40,6 +48,17 @@ class ModuleBluetooth final : public Module { ndk::ScopedAStatus getMicMute(bool* _aidl_return) override; ndk::ScopedAStatus setMicMute(bool in_mute) override; + ndk::ScopedAStatus setAudioPortConfig( + const ::aidl::android::media::audio::common::AudioPortConfig& in_requested, + ::aidl::android::media::audio::common::AudioPortConfig* out_suggested, + bool* _aidl_return) override; + + ndk::ScopedAStatus checkAudioPatchEndpointsMatch( + const std::vector<::aidl::android::media::audio::common::AudioPortConfig*>& sources, + const std::vector<::aidl::android::media::audio::common::AudioPortConfig*>& sinks) + override; + void onExternalDeviceConnectionChanged( + const ::aidl::android::media::audio::common::AudioPort& audioPort, bool connected); ndk::ScopedAStatus createInputStream( StreamContext&& context, const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata, @@ -52,12 +71,24 @@ class ModuleBluetooth final : public Module { offloadInfo, std::shared_ptr* result) override; ndk::ScopedAStatus populateConnectedDevicePort( - ::aidl::android::media::audio::common::AudioPort* audioPort) override; + ::aidl::android::media::audio::common::AudioPort* audioPort, + int32_t nextPortId) override; ndk::ScopedAStatus onMasterMuteChanged(bool mute) override; ndk::ScopedAStatus onMasterVolumeChanged(float volume) override; + int32_t getNominalLatencyMs( + const ::aidl::android::media::audio::common::AudioPortConfig& portConfig) override; + + ndk::ScopedAStatus createProxy( + const ::aidl::android::media::audio::common::AudioPort& audioPort, + int32_t instancePortId, CachedProxy& proxy); + ndk::ScopedAStatus fetchAndCheckProxy(const StreamContext& context, CachedProxy& proxy); + ndk::ScopedAStatus findOrCreateProxy( + const ::aidl::android::media::audio::common::AudioPort& audioPort, CachedProxy& proxy); ChildInterface mBluetoothA2dp; ChildInterface mBluetoothLe; + std::map mProxies; + std::map mConnections; }; } // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h b/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h index 9f8acc9e1d..613ac6209c 100644 --- a/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h +++ b/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h @@ -29,6 +29,10 @@ class ModuleRemoteSubmix : public Module { // IModule interfaces ndk::ScopedAStatus getMicMute(bool* _aidl_return) override; ndk::ScopedAStatus setMicMute(bool in_mute) override; + ndk::ScopedAStatus setAudioPortConfig( + const ::aidl::android::media::audio::common::AudioPortConfig& in_requested, + ::aidl::android::media::audio::common::AudioPortConfig* out_suggested, + bool* _aidl_return) override; // Module interfaces ndk::ScopedAStatus createInputStream( @@ -43,7 +47,8 @@ class ModuleRemoteSubmix : public Module { offloadInfo, std::shared_ptr* result) override; ndk::ScopedAStatus populateConnectedDevicePort( - ::aidl::android::media::audio::common::AudioPort* audioPort) override; + ::aidl::android::media::audio::common::AudioPort* audioPort, + int32_t nextPortId) override; ndk::ScopedAStatus checkAudioPatchEndpointsMatch( const std::vector<::aidl::android::media::audio::common::AudioPortConfig*>& sources, const std::vector<::aidl::android::media::audio::common::AudioPortConfig*>& sinks) diff --git a/audio/aidl/default/include/core-impl/ModuleUsb.h b/audio/aidl/default/include/core-impl/ModuleUsb.h index 6ee8f8a691..d9ac4f015b 100644 --- a/audio/aidl/default/include/core-impl/ModuleUsb.h +++ b/audio/aidl/default/include/core-impl/ModuleUsb.h @@ -44,7 +44,8 @@ class ModuleUsb final : public ModuleAlsa { offloadInfo, std::shared_ptr* result) override; ndk::ScopedAStatus populateConnectedDevicePort( - ::aidl::android::media::audio::common::AudioPort* audioPort) override; + ::aidl::android::media::audio::common::AudioPort* audioPort, + int32_t nextPortId) override; ndk::ScopedAStatus checkAudioPatchEndpointsMatch( const std::vector<::aidl::android::media::audio::common::AudioPortConfig*>& sources, const std::vector<::aidl::android::media::audio::common::AudioPortConfig*>& sinks) diff --git a/audio/aidl/default/include/core-impl/StreamBluetooth.h b/audio/aidl/default/include/core-impl/StreamBluetooth.h index 1258d3860c..35c3183560 100644 --- a/audio/aidl/default/include/core-impl/StreamBluetooth.h +++ b/audio/aidl/default/include/core-impl/StreamBluetooth.h @@ -31,8 +31,16 @@ namespace aidl::android::hardware::audio::core { class StreamBluetooth : public StreamCommonImpl { public: - StreamBluetooth(StreamContext* context, const Metadata& metadata, - ModuleBluetooth::BtProfileHandles&& btHandles); + static bool checkConfigParams( + const ::aidl::android::hardware::bluetooth::audio::PcmConfiguration& pcmConfig, + const ::aidl::android::media::audio::common::AudioConfigBase& config); + + StreamBluetooth( + StreamContext* context, const Metadata& metadata, + ModuleBluetooth::BtProfileHandles&& btHandles, + const std::shared_ptr<::android::bluetooth::audio::aidl::BluetoothAudioPortAidl>& + btDeviceProxy, + const ::aidl::android::hardware::bluetooth::audio::PcmConfiguration& pcmConfig); // Methods of 'DriverInterface'. ::android::status_t init() override; ::android::status_t drain(StreamDescriptor::DrainMode) override; @@ -47,40 +55,35 @@ class StreamBluetooth : public StreamCommonImpl { // Overridden methods of 'StreamCommonImpl', called on a Binder thread. ndk::ScopedAStatus updateMetadataCommon(const Metadata& metadata) override; ndk::ScopedAStatus prepareToClose() override; - const ConnectedDevices& getConnectedDevices() const override; - ndk::ScopedAStatus setConnectedDevices(const ConnectedDevices& devices) override; ndk::ScopedAStatus bluetoothParametersUpdated() override; private: - // Audio Pcm Config - const uint32_t mSampleRate; - const ::aidl::android::media::audio::common::AudioChannelLayout mChannelLayout; - const ::aidl::android::media::audio::common::AudioFormatDescription mFormat; const size_t mFrameSizeBytes; const bool mIsInput; const std::weak_ptr mBluetoothA2dp; const std::weak_ptr mBluetoothLe; - size_t mPreferredDataIntervalUs; - size_t mPreferredFrameCount; - + const size_t mPreferredDataIntervalUs; + const size_t mPreferredFrameCount; mutable std::mutex mLock; - bool mIsInitialized GUARDED_BY(mLock); - bool mIsReadyToClose GUARDED_BY(mLock); - std::vector> - mBtDeviceProxies GUARDED_BY(mLock); - - ::android::status_t initialize() REQUIRES(mLock); - bool checkConfigParams(::aidl::android::hardware::bluetooth::audio::PcmConfiguration& config); + // The lock is also used to serialize calls to the proxy. + std::shared_ptr<::android::bluetooth::audio::aidl::BluetoothAudioPortAidl> mBtDeviceProxy + GUARDED_BY(mLock); // proxy may be null if the stream is not connected to a device }; class StreamInBluetooth final : public StreamIn, public StreamBluetooth { public: friend class ndk::SharedRefBase; + + static int32_t getNominalLatencyMs(size_t dataIntervalUs); + StreamInBluetooth( StreamContext&& context, const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata, const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones, - ModuleBluetooth::BtProfileHandles&& btHandles); + ModuleBluetooth::BtProfileHandles&& btHandles, + const std::shared_ptr<::android::bluetooth::audio::aidl::BluetoothAudioPortAidl>& + btDeviceProxy, + const ::aidl::android::hardware::bluetooth::audio::PcmConfiguration& pcmConfig); private: void onClose(StreamDescriptor::State) override { defaultOnClose(); } @@ -92,12 +95,18 @@ class StreamInBluetooth final : public StreamIn, public StreamBluetooth { class StreamOutBluetooth final : public StreamOut, public StreamBluetooth { public: friend class ndk::SharedRefBase; + + static int32_t getNominalLatencyMs(size_t dataIntervalUs); + StreamOutBluetooth( StreamContext&& context, const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata, const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>& offloadInfo, - ModuleBluetooth::BtProfileHandles&& btHandles); + ModuleBluetooth::BtProfileHandles&& btHandles, + const std::shared_ptr<::android::bluetooth::audio::aidl::BluetoothAudioPortAidl>& + btDeviceProxy, + const ::aidl::android::hardware::bluetooth::audio::PcmConfiguration& pcmConfig); private: void onClose(StreamDescriptor::State) override { defaultOnClose(); } diff --git a/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h b/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h index ee10abf087..cc06881c7b 100644 --- a/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h +++ b/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h @@ -16,7 +16,6 @@ #pragma once -#include #include #include "core-impl/Stream.h" @@ -56,13 +55,6 @@ class StreamRemoteSubmix : public StreamCommonImpl { r_submix::AudioConfig mStreamConfig; std::shared_ptr mCurrentRoute = nullptr; - // Mutex lock to protect vector of submix routes, each of these submix routes have their mutex - // locks and none of the mutex locks should be taken together. - static std::mutex sSubmixRoutesLock; - static std::map<::aidl::android::media::audio::common::AudioDeviceAddress, - std::shared_ptr> - sSubmixRoutes GUARDED_BY(sSubmixRoutesLock); - // limit for number of read error log entries to avoid spamming the logs static constexpr int kMaxReadErrorLogs = 5; // The duration of kMaxReadFailureAttempts * READ_ATTEMPT_SLEEP_MS must be strictly inferior diff --git a/audio/aidl/default/r_submix/ModuleRemoteSubmix.cpp b/audio/aidl/default/r_submix/ModuleRemoteSubmix.cpp index f3965ba9eb..47ade4981f 100644 --- a/audio/aidl/default/r_submix/ModuleRemoteSubmix.cpp +++ b/audio/aidl/default/r_submix/ModuleRemoteSubmix.cpp @@ -27,14 +27,36 @@ using aidl::android::hardware::audio::common::SinkMetadata; using aidl::android::hardware::audio::common::SourceMetadata; +using aidl::android::media::audio::common::AudioDeviceAddress; using aidl::android::media::audio::common::AudioFormatType; +using aidl::android::media::audio::common::AudioIoFlags; using aidl::android::media::audio::common::AudioOffloadInfo; using aidl::android::media::audio::common::AudioPort; using aidl::android::media::audio::common::AudioPortConfig; +using aidl::android::media::audio::common::AudioPortExt; +using aidl::android::media::audio::common::AudioProfile; +using aidl::android::media::audio::common::Int; using aidl::android::media::audio::common::MicrophoneInfo; namespace aidl::android::hardware::audio::core { +namespace { + +std::optional getRemoteEndConfig(const AudioPort& audioPort) { + const auto& deviceAddress = audioPort.ext.get().device.address; + const bool isInput = audioPort.flags.getTag() == AudioIoFlags::input; + if (auto submixRoute = r_submix::SubmixRoute::findRoute(deviceAddress); + submixRoute != nullptr) { + if ((isInput && submixRoute->isStreamOutOpen()) || + (!isInput && submixRoute->isStreamInOpen())) { + return submixRoute->getPipeConfig(); + } + } + return {}; +} + +} // namespace + ndk::ScopedAStatus ModuleRemoteSubmix::getMicMute(bool* _aidl_return __unused) { LOG(DEBUG) << __func__ << ": is not supported"; return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); @@ -45,6 +67,26 @@ ndk::ScopedAStatus ModuleRemoteSubmix::setMicMute(bool in_mute __unused) { return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } +ndk::ScopedAStatus ModuleRemoteSubmix::setAudioPortConfig(const AudioPortConfig& in_requested, + AudioPortConfig* out_suggested, + bool* _aidl_return) { + auto fillConfig = [this](const AudioPort& port, AudioPortConfig* config) { + if (port.ext.getTag() == AudioPortExt::device) { + if (auto pipeConfig = getRemoteEndConfig(port); pipeConfig.has_value()) { + LOG(DEBUG) << "setAudioPortConfig: suggesting port config from the remote end."; + config->format = pipeConfig->format; + config->channelMask = pipeConfig->channelLayout; + config->sampleRate = Int{.value = pipeConfig->sampleRate}; + config->flags = port.flags; + config->ext = port.ext; + return true; + } + } + return generateDefaultPortConfig(port, config); + }; + return Module::setAudioPortConfigImpl(in_requested, fillConfig, out_suggested, _aidl_return); +} + ndk::ScopedAStatus ModuleRemoteSubmix::createInputStream( StreamContext&& context, const SinkMetadata& sinkMetadata, const std::vector& microphones, std::shared_ptr* result) { @@ -67,8 +109,23 @@ ndk::ScopedAStatus ModuleRemoteSubmix::createOutputStream( offloadInfo); } -ndk::ScopedAStatus ModuleRemoteSubmix::populateConnectedDevicePort(AudioPort* audioPort) { - // Find the corresponding mix port and copy its profiles. +ndk::ScopedAStatus ModuleRemoteSubmix::populateConnectedDevicePort(AudioPort* audioPort, int32_t) { + if (audioPort->ext.getTag() != AudioPortExt::device) { + LOG(ERROR) << __func__ << ": not a device port: " << audioPort->toString(); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + // If there is already a pipe with a stream for the port address, provide its configuration as + // the only option. Otherwise, find the corresponding mix port and copy its profiles. + if (auto pipeConfig = getRemoteEndConfig(*audioPort); pipeConfig.has_value()) { + audioPort->profiles.clear(); + audioPort->profiles.push_back(AudioProfile{ + .format = pipeConfig->format, + .channelMasks = std::vector({pipeConfig->channelLayout}), + .sampleRates = std::vector({pipeConfig->sampleRate})}); + LOG(DEBUG) << __func__ << ": populated from remote end as: " << audioPort->toString(); + return ndk::ScopedAStatus::ok(); + } + // At this moment, the port has the same ID as the template port, see connectExternalDevice. std::vector routes = getAudioRoutesForAudioPortImpl(audioPort->id); if (routes.empty()) { @@ -87,6 +144,7 @@ ndk::ScopedAStatus ModuleRemoteSubmix::populateConnectedDevicePort(AudioPort* au RETURN_STATUS_IF_ERROR(getAudioPort(route->sinkPortId, &mixPort)); } audioPort->profiles = mixPort.profiles; + LOG(DEBUG) << __func__ << ": populated from the mix port as: " << audioPort->toString(); return ndk::ScopedAStatus::ok(); } diff --git a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp index d238aa42b3..df706ac026 100644 --- a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp +++ b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp @@ -43,26 +43,10 @@ StreamRemoteSubmix::StreamRemoteSubmix(StreamContext* context, const Metadata& m mStreamConfig.sampleRate = context->getSampleRate(); } -std::mutex StreamRemoteSubmix::sSubmixRoutesLock; -std::map> StreamRemoteSubmix::sSubmixRoutes; - ::android::status_t StreamRemoteSubmix::init() { - { - std::lock_guard guard(sSubmixRoutesLock); - auto routeItr = sSubmixRoutes.find(mDeviceAddress); - if (routeItr != sSubmixRoutes.end()) { - mCurrentRoute = routeItr->second; - } - // If route is not available for this port, add it. - if (mCurrentRoute == nullptr) { - // Initialize the pipe. - mCurrentRoute = std::make_shared(); - if (::android::OK != mCurrentRoute->createPipe(mStreamConfig)) { - LOG(ERROR) << __func__ << ": create pipe failed"; - return ::android::NO_INIT; - } - sSubmixRoutes.emplace(mDeviceAddress, mCurrentRoute); - } + mCurrentRoute = SubmixRoute::findOrCreateRoute(mDeviceAddress, mStreamConfig); + if (mCurrentRoute == nullptr) { + return ::android::NO_INIT; } if (!mCurrentRoute->isStreamConfigValid(mIsInput, mStreamConfig)) { LOG(ERROR) << __func__ << ": invalid stream config"; @@ -80,7 +64,6 @@ std::map> StreamRemoteSubmix::s return ::android::NO_INIT; } } - mCurrentRoute->openStream(mIsInput); return ::android::OK; } @@ -114,14 +97,7 @@ std::map> StreamRemoteSubmix::s ndk::ScopedAStatus StreamRemoteSubmix::prepareToClose() { if (!mIsInput) { - std::shared_ptr route = nullptr; - { - std::lock_guard guard(sSubmixRoutesLock); - auto routeItr = sSubmixRoutes.find(mDeviceAddress); - if (routeItr != sSubmixRoutes.end()) { - route = routeItr->second; - } - } + std::shared_ptr route = SubmixRoute::findRoute(mDeviceAddress); if (route != nullptr) { sp sink = route->getSink(); if (sink == nullptr) { @@ -148,9 +124,7 @@ void StreamRemoteSubmix::shutdown() { if (!mCurrentRoute->hasAtleastOneStreamOpen()) { mCurrentRoute->releasePipe(); LOG(DEBUG) << __func__ << ": pipe destroyed"; - - std::lock_guard guard(sSubmixRoutesLock); - sSubmixRoutes.erase(mDeviceAddress); + SubmixRoute::removeRoute(mDeviceAddress); } mCurrentRoute.reset(); } @@ -201,7 +175,7 @@ long StreamRemoteSubmix::getDelayInUsForFrameCount(size_t frameCount) { // Calculate the maximum size of the pipe buffer in frames for the specified stream. size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() { - auto pipeConfig = mCurrentRoute->mPipeConfig; + auto pipeConfig = mCurrentRoute->getPipeConfig(); const size_t maxFrameSize = std::max(mStreamConfig.frameSize, pipeConfig.frameSize); return (pipeConfig.frameCount * pipeConfig.frameSize) / maxFrameSize; } diff --git a/audio/aidl/default/r_submix/SubmixRoute.cpp b/audio/aidl/default/r_submix/SubmixRoute.cpp index 235c9a3f32..7d706c27ce 100644 --- a/audio/aidl/default/r_submix/SubmixRoute.cpp +++ b/audio/aidl/default/r_submix/SubmixRoute.cpp @@ -23,9 +23,49 @@ #include "SubmixRoute.h" using aidl::android::hardware::audio::common::getChannelCount; +using aidl::android::media::audio::common::AudioDeviceAddress; namespace aidl::android::hardware::audio::core::r_submix { +// static +SubmixRoute::RoutesMonitor SubmixRoute::getRoutes() { + static std::mutex submixRoutesLock; + static RoutesMap submixRoutes; + return RoutesMonitor(submixRoutesLock, submixRoutes); +} + +// static +std::shared_ptr SubmixRoute::findOrCreateRoute(const AudioDeviceAddress& deviceAddress, + const AudioConfig& pipeConfig) { + auto routes = getRoutes(); + auto routeItr = routes->find(deviceAddress); + if (routeItr != routes->end()) { + return routeItr->second; + } + auto route = std::make_shared(); + if (::android::OK != route->createPipe(pipeConfig)) { + LOG(ERROR) << __func__ << ": create pipe failed"; + return nullptr; + } + routes->emplace(deviceAddress, route); + return route; +} + +// static +std::shared_ptr SubmixRoute::findRoute(const AudioDeviceAddress& deviceAddress) { + auto routes = getRoutes(); + auto routeItr = routes->find(deviceAddress); + if (routeItr != routes->end()) { + return routeItr->second; + } + return nullptr; +} + +// static +void SubmixRoute::removeRoute(const AudioDeviceAddress& deviceAddress) { + getRoutes()->erase(deviceAddress); +} + // Verify a submix input or output stream can be opened. bool SubmixRoute::isStreamConfigValid(bool isInput, const AudioConfig& streamConfig) { // If the stream is already open, don't open it again. @@ -44,6 +84,7 @@ bool SubmixRoute::isStreamConfigValid(bool isInput, const AudioConfig& streamCon // Compare this stream config with existing pipe config, returning false if they do *not* // match, true otherwise. bool SubmixRoute::isStreamConfigCompatible(const AudioConfig& streamConfig) { + std::lock_guard guard(mLock); if (streamConfig.channelLayout != mPipeConfig.channelLayout) { LOG(ERROR) << __func__ << ": channel count mismatch, stream channels = " << streamConfig.channelLayout.toString() @@ -162,17 +203,14 @@ void SubmixRoute::closeStream(bool isInput) { LOG(FATAL) << __func__ << ": Negotiation for the source failed, index = " << index; return ::android::BAD_INDEX; } - LOG(VERBOSE) << __func__ << ": created pipe"; - - mPipeConfig = streamConfig; - mPipeConfig.frameCount = sink->maxFrames(); - - LOG(VERBOSE) << __func__ << ": Pipe frame size : " << mPipeConfig.frameSize - << ", pipe frames : " << mPipeConfig.frameCount; + LOG(VERBOSE) << __func__ << ": Pipe frame size : " << streamConfig.frameSize + << ", pipe frames : " << sink->maxFrames(); // Save references to the source and sink. { std::lock_guard guard(mLock); + mPipeConfig = streamConfig; + mPipeConfig.frameCount = sink->maxFrames(); mSink = std::move(sink); mSource = std::move(source); } @@ -181,15 +219,15 @@ void SubmixRoute::closeStream(bool isInput) { } // Release references to the sink and source. -void SubmixRoute::releasePipe() { +AudioConfig SubmixRoute::releasePipe() { std::lock_guard guard(mLock); mSink.clear(); mSource.clear(); + return mPipeConfig; } ::android::status_t SubmixRoute::resetPipe() { - releasePipe(); - return createPipe(mPipeConfig); + return createPipe(releasePipe()); } void SubmixRoute::standby(bool isInput) { diff --git a/audio/aidl/default/r_submix/SubmixRoute.h b/audio/aidl/default/r_submix/SubmixRoute.h index 252b1c9524..160df41265 100644 --- a/audio/aidl/default/r_submix/SubmixRoute.h +++ b/audio/aidl/default/r_submix/SubmixRoute.h @@ -25,6 +25,7 @@ #include #include +#include #include using aidl::android::media::audio::common::AudioChannelLayout; @@ -60,7 +61,13 @@ struct AudioConfig { class SubmixRoute { public: - AudioConfig mPipeConfig; + static std::shared_ptr findOrCreateRoute( + const ::aidl::android::media::audio::common::AudioDeviceAddress& deviceAddress, + const AudioConfig& pipeConfig); + static std::shared_ptr findRoute( + const ::aidl::android::media::audio::common::AudioDeviceAddress& deviceAddress); + static void removeRoute( + const ::aidl::android::media::audio::common::AudioDeviceAddress& deviceAddress); bool isStreamInOpen() { std::lock_guard guard(mLock); @@ -90,6 +97,10 @@ class SubmixRoute { std::lock_guard guard(mLock); return mSource; } + AudioConfig getPipeConfig() { + std::lock_guard guard(mLock); + return mPipeConfig; + } bool isStreamConfigValid(bool isInput, const AudioConfig& streamConfig); void closeStream(bool isInput); @@ -98,17 +109,31 @@ class SubmixRoute { bool hasAtleastOneStreamOpen(); int notifyReadError(); void openStream(bool isInput); - void releasePipe(); + AudioConfig releasePipe(); ::android::status_t resetPipe(); bool shouldBlockWrite(); void standby(bool isInput); long updateReadCounterFrames(size_t frameCount); private: + using RoutesMap = std::map<::aidl::android::media::audio::common::AudioDeviceAddress, + std::shared_ptr>; + class RoutesMonitor { + public: + RoutesMonitor(std::mutex& mutex, RoutesMap& routes) : mLock(mutex), mRoutes(routes) {} + RoutesMap* operator->() { return &mRoutes; } + + private: + std::lock_guard mLock; + RoutesMap& mRoutes; + }; + + static RoutesMonitor getRoutes(); + bool isStreamConfigCompatible(const AudioConfig& streamConfig); std::mutex mLock; - + AudioConfig mPipeConfig GUARDED_BY(mLock); bool mStreamInOpen GUARDED_BY(mLock) = false; int mInputRefCount GUARDED_BY(mLock) = 0; bool mStreamInStandby GUARDED_BY(mLock) = true; diff --git a/audio/aidl/default/usb/ModuleUsb.cpp b/audio/aidl/default/usb/ModuleUsb.cpp index f926e09399..1d97bc4aa8 100644 --- a/audio/aidl/default/usb/ModuleUsb.cpp +++ b/audio/aidl/default/usb/ModuleUsb.cpp @@ -87,12 +87,13 @@ ndk::ScopedAStatus ModuleUsb::createOutputStream(StreamContext&& context, offloadInfo); } -ndk::ScopedAStatus ModuleUsb::populateConnectedDevicePort(AudioPort* audioPort) { +ndk::ScopedAStatus ModuleUsb::populateConnectedDevicePort(AudioPort* audioPort, + int32_t nextPortId) { if (!isUsbDevicePort(*audioPort)) { LOG(ERROR) << __func__ << ": port id " << audioPort->id << " is not a usb device port"; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } - return ModuleAlsa::populateConnectedDevicePort(audioPort); + return ModuleAlsa::populateConnectedDevicePort(audioPort, nextPortId); } ndk::ScopedAStatus ModuleUsb::checkAudioPatchEndpointsMatch( diff --git a/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp b/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp index c05750538d..67ba93cdd7 100644 --- a/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp +++ b/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp @@ -500,14 +500,12 @@ bool BluetoothAudioSession::GetPresentationPosition( << " has NO session"; return false; } - bool retval = false; - if (!stack_iface_->getPresentationPosition(&presentation_position).isOk()) { LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType=" << toString(session_type_) << " failed"; return false; } - return retval; + return true; } void BluetoothAudioSession::UpdateSourceMetadata(