From a92039ac4855398209dd171e0552b8fd33568515 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Wed, 20 Dec 2023 14:27:22 -0800 Subject: [PATCH 1/3] audio: Refactor configuration population In 'Module::setAudioPortConfig' allow subclasses to provide their own suggested configuration. In 'Module::populateConnectedDevicePort' provide the ID of the device port instance that will be created as a result of connecting an external device. Also, expose 'ModuleDebug' flags to subclasses so that they can know when device connections are simulated. Bug: 264712385 Test: atest VtsHalAudioCoreTargetTest Change-Id: Iceb2bdeb61c3470554b592fe197efa54d3b9e578 --- audio/aidl/default/Module.cpp | 84 +++++++++++-------- audio/aidl/default/alsa/ModuleAlsa.cpp | 2 +- .../default/bluetooth/ModuleBluetooth.cpp | 2 +- audio/aidl/default/include/core-impl/Module.h | 14 +++- .../default/include/core-impl/ModuleAlsa.h | 3 +- .../include/core-impl/ModuleBluetooth.h | 3 +- .../include/core-impl/ModuleRemoteSubmix.h | 3 +- .../default/include/core-impl/ModuleUsb.h | 3 +- .../default/r_submix/ModuleRemoteSubmix.cpp | 2 +- audio/aidl/default/usb/ModuleUsb.cpp | 5 +- 10 files changed, 75 insertions(+), 46 deletions(-) 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/ModuleBluetooth.cpp b/audio/aidl/default/bluetooth/ModuleBluetooth.cpp index 8a1cbbfe1c..03abd34f4c 100644 --- a/audio/aidl/default/bluetooth/ModuleBluetooth.cpp +++ b/audio/aidl/default/bluetooth/ModuleBluetooth.cpp @@ -109,7 +109,7 @@ ndk::ScopedAStatus ModuleBluetooth::createOutputStream( offloadInfo, getBtProfileManagerHandles()); } -ndk::ScopedAStatus ModuleBluetooth::populateConnectedDevicePort(AudioPort* audioPort) { +ndk::ScopedAStatus ModuleBluetooth::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/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..e48526e292 100644 --- a/audio/aidl/default/include/core-impl/ModuleBluetooth.h +++ b/audio/aidl/default/include/core-impl/ModuleBluetooth.h @@ -52,7 +52,8 @@ 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; diff --git a/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h b/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h index 9f8acc9e1d..e89c6edd03 100644 --- a/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h +++ b/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h @@ -43,7 +43,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/r_submix/ModuleRemoteSubmix.cpp b/audio/aidl/default/r_submix/ModuleRemoteSubmix.cpp index f3965ba9eb..1a5ee00885 100644 --- a/audio/aidl/default/r_submix/ModuleRemoteSubmix.cpp +++ b/audio/aidl/default/r_submix/ModuleRemoteSubmix.cpp @@ -67,7 +67,7 @@ ndk::ScopedAStatus ModuleRemoteSubmix::createOutputStream( offloadInfo); } -ndk::ScopedAStatus ModuleRemoteSubmix::populateConnectedDevicePort(AudioPort* audioPort) { +ndk::ScopedAStatus ModuleRemoteSubmix::populateConnectedDevicePort(AudioPort* audioPort, int32_t) { // Find the corresponding mix port and copy its profiles. // At this moment, the port has the same ID as the template port, see connectExternalDevice. std::vector routes = getAudioRoutesForAudioPortImpl(audioPort->id); 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( From 3b732895a82c18f86d43dc8fa06c3754baa3730a Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Wed, 20 Dec 2023 16:05:01 -0800 Subject: [PATCH 2/3] audio r_submix: Suggest configuration from the peer When there is a pipe established for a remote submix device, suggest the configuration of the peer when opening the other end. Refactor SubmixRoute management to move it out from StreamRemoteSubmix so that ModuleRemoteSubmix could also use it. Bug: 294962274 Test: atest audiorouting_tests Change-Id: Ib31a662e7b65b92c614dc441a01160cae3485f3a --- .../include/core-impl/ModuleRemoteSubmix.h | 4 ++ .../include/core-impl/StreamRemoteSubmix.h | 8 --- .../default/r_submix/ModuleRemoteSubmix.cpp | 60 ++++++++++++++++++- .../default/r_submix/StreamRemoteSubmix.cpp | 38 ++---------- audio/aidl/default/r_submix/SubmixRoute.cpp | 58 ++++++++++++++---- audio/aidl/default/r_submix/SubmixRoute.h | 31 +++++++++- 6 files changed, 145 insertions(+), 54 deletions(-) diff --git a/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h b/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h index e89c6edd03..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( 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 1a5ee00885..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) { @@ -68,7 +110,22 @@ ndk::ScopedAStatus ModuleRemoteSubmix::createOutputStream( } ndk::ScopedAStatus ModuleRemoteSubmix::populateConnectedDevicePort(AudioPort* audioPort, int32_t) { - // Find the corresponding mix port and copy its profiles. + 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; From a88cf60b87b23d813e9c1ddd32fa892034b5f821 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Wed, 13 Dec 2023 14:35:11 -0800 Subject: [PATCH 3/3] audio: Fix BT AIDL HAL module implementation In order to align with legacy behavior, when opening a stream, the module must suggest the current configuration of the BT session. For that to work, the BT device proxy must be opened prior to creating a stream, code moved to ModuleBluetooth. Fix minor inconsistencies and bugs found during testing. Bug: 301213930 Bug: 316027906 Test: atest pts-bot Change-Id: I04ddaf73be82f872a3f32a789563c3cbd648eb61 --- .../default/bluetooth/DevicePortProxy.cpp | 37 +-- .../default/bluetooth/ModuleBluetooth.cpp | 232 +++++++++++++-- .../default/bluetooth/StreamBluetooth.cpp | 270 +++++++----------- .../include/core-impl/DevicePortProxy.h | 20 +- .../include/core-impl/ModuleBluetooth.h | 30 ++ .../include/core-impl/StreamBluetooth.h | 49 ++-- .../aidl_session/BluetoothAudioSession.cpp | 4 +- 7 files changed, 397 insertions(+), 245 deletions(-) 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 03abd34f4c..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, int32_t) { +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/ModuleBluetooth.h b/audio/aidl/default/include/core-impl/ModuleBluetooth.h index e48526e292..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, @@ -56,9 +75,20 @@ class ModuleBluetooth final : public Module { 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/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/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(