audio: Do not use StreamSwitcher for StreamRemoteSubmix
Since use of StreamSwitcher causes the worker thread to be changed
during connected device change, its use must be avoided. We intend
to remove StreamSwitcher completely in future.
Bug: 300130515
Bug: 338974476
Bug: 368723297
Bug: 369272078
Bug: 369289912
Bug: 369964381
Test: atest CtsMediaAudioTestCases
Test: atest VtsHalAudioCoreTargetTest
Change-Id: I4f6fd35f69d73641a86e1102f1d30d5e8f626e8f
diff --git a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
index db105b6..93fe028 100644
--- a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
+++ b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
@@ -26,128 +26,106 @@
using aidl::android::hardware::audio::common::SourceMetadata;
using aidl::android::hardware::audio::core::r_submix::SubmixRoute;
using aidl::android::media::audio::common::AudioDeviceAddress;
+using aidl::android::media::audio::common::AudioDeviceType;
using aidl::android::media::audio::common::AudioOffloadInfo;
using aidl::android::media::audio::common::MicrophoneDynamicInfo;
using aidl::android::media::audio::common::MicrophoneInfo;
namespace aidl::android::hardware::audio::core {
-StreamRemoteSubmix::StreamRemoteSubmix(StreamContext* context, const Metadata& metadata,
- const AudioDeviceAddress& deviceAddress)
+StreamRemoteSubmix::StreamRemoteSubmix(StreamContext* context, const Metadata& metadata)
: StreamCommonImpl(context, metadata),
- mDeviceAddress(deviceAddress),
- mIsInput(isInput(metadata)) {
- mStreamConfig.frameSize = context->getFrameSize();
- mStreamConfig.format = context->getFormat();
- mStreamConfig.channelLayout = context->getChannelLayout();
- mStreamConfig.sampleRate = context->getSampleRate();
-}
+ mIsInput(isInput(metadata)),
+ mStreamConfig{.sampleRate = context->getSampleRate(),
+ .format = context->getFormat(),
+ .channelLayout = context->getChannelLayout(),
+ .frameSize = context->getFrameSize()} {}
StreamRemoteSubmix::~StreamRemoteSubmix() {
cleanupWorker();
}
::android::status_t StreamRemoteSubmix::init() {
- mCurrentRoute = SubmixRoute::findOrCreateRoute(mDeviceAddress, mStreamConfig);
- if (mCurrentRoute == nullptr) {
- return ::android::NO_INIT;
- }
- if (!mCurrentRoute->isStreamConfigValid(mIsInput, mStreamConfig)) {
- LOG(ERROR) << __func__ << ": invalid stream config";
- return ::android::NO_INIT;
- }
- sp<MonoPipe> sink = mCurrentRoute->getSink();
- if (sink == nullptr) {
- LOG(ERROR) << __func__ << ": nullptr sink when opening stream";
- return ::android::NO_INIT;
- }
- if ((!mIsInput || mCurrentRoute->isStreamInOpen()) && sink->isShutdown()) {
- LOG(DEBUG) << __func__ << ": Shut down sink when opening stream";
- if (::android::OK != mCurrentRoute->resetPipe()) {
- LOG(ERROR) << __func__ << ": reset pipe failed";
- return ::android::NO_INIT;
- }
- }
- mCurrentRoute->openStream(mIsInput);
return ::android::OK;
}
::android::status_t StreamRemoteSubmix::drain(StreamDescriptor::DrainMode) {
- usleep(1000);
return ::android::OK;
}
::android::status_t StreamRemoteSubmix::flush() {
- usleep(1000);
return ::android::OK;
}
::android::status_t StreamRemoteSubmix::pause() {
- usleep(1000);
return ::android::OK;
}
::android::status_t StreamRemoteSubmix::standby() {
- mCurrentRoute->standby(mIsInput);
+ if (mCurrentRoute) mCurrentRoute->standby(mIsInput);
return ::android::OK;
}
::android::status_t StreamRemoteSubmix::start() {
- mCurrentRoute->exitStandby(mIsInput);
+ if (mDeviceAddressUpdated.load(std::memory_order_acquire)) {
+ LOG(DEBUG) << __func__ << ": device address updated, reset current route";
+ shutdown();
+ mDeviceAddressUpdated.store(false, std::memory_order_release);
+ }
+ if (!mCurrentRoute) {
+ RETURN_STATUS_IF_ERROR(setCurrentRoute());
+ LOG(DEBUG) << __func__ << ": have current route? " << (mCurrentRoute != nullptr);
+ }
+ if (mCurrentRoute) mCurrentRoute->exitStandby(mIsInput);
mStartTimeNs = ::android::uptimeNanos();
mFramesSinceStart = 0;
return ::android::OK;
}
-ndk::ScopedAStatus StreamRemoteSubmix::prepareToClose() {
- if (!mIsInput) {
- std::shared_ptr<SubmixRoute> route = SubmixRoute::findRoute(mDeviceAddress);
- if (route != nullptr) {
- sp<MonoPipe> sink = route->getSink();
- if (sink == nullptr) {
- ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
- }
- LOG(DEBUG) << __func__ << ": shutting down MonoPipe sink";
-
- sink->shutdown(true);
- // The client already considers this stream as closed, release the output end.
- route->closeStream(mIsInput);
- } else {
- LOG(DEBUG) << __func__ << ": stream already closed.";
- ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
- }
- }
- return ndk::ScopedAStatus::ok();
-}
-
// Remove references to the specified input and output streams. When the device no longer
// references input and output streams destroy the associated pipe.
void StreamRemoteSubmix::shutdown() {
+ if (!mCurrentRoute) return;
mCurrentRoute->closeStream(mIsInput);
// If all stream instances are closed, we can remove route information for this port.
if (!mCurrentRoute->hasAtleastOneStreamOpen()) {
mCurrentRoute->releasePipe();
LOG(DEBUG) << __func__ << ": pipe destroyed";
- SubmixRoute::removeRoute(mDeviceAddress);
+ SubmixRoute::removeRoute(getDeviceAddress());
}
mCurrentRoute.reset();
}
::android::status_t StreamRemoteSubmix::transfer(void* buffer, size_t frameCount,
size_t* actualFrameCount, int32_t* latencyMs) {
+ if (mDeviceAddressUpdated.load(std::memory_order_acquire)) {
+ // 'setConnectedDevices' was called. I/O will be restarted.
+ return ::android::OK;
+ }
+
*latencyMs = getDelayInUsForFrameCount(getStreamPipeSizeInFrames()) / 1000;
LOG(VERBOSE) << __func__ << ": Latency " << *latencyMs << "ms";
- mCurrentRoute->exitStandby(mIsInput);
- ::android::status_t status = mIsInput ? inRead(buffer, frameCount, actualFrameCount)
- : outWrite(buffer, frameCount, actualFrameCount);
- if ((status != ::android::OK && mIsInput) ||
- ((status != ::android::OK && status != ::android::DEAD_OBJECT) && !mIsInput)) {
- return status;
+ ::android::status_t status = ::android::OK;
+ if (mCurrentRoute) {
+ mCurrentRoute->exitStandby(mIsInput);
+ status = mIsInput ? inRead(buffer, frameCount, actualFrameCount)
+ : outWrite(buffer, frameCount, actualFrameCount);
+ if ((status != ::android::OK && mIsInput) ||
+ ((status != ::android::OK && status != ::android::DEAD_OBJECT) && !mIsInput)) {
+ return status;
+ }
+ } else {
+ LOG(WARNING) << __func__ << ": no current route";
+ if (mIsInput) {
+ memset(buffer, 0, mStreamConfig.frameSize * frameCount);
+ }
+ *actualFrameCount = frameCount;
}
mFramesSinceStart += *actualFrameCount;
- if (!mIsInput && status != ::android::DEAD_OBJECT) return ::android::OK;
- // Input streams always need to block, output streams need to block when there is no sink.
- // When the sink exists, more sophisticated blocking algorithm is implemented by MonoPipe.
+ // If there is no route, always block, otherwise:
+ // - Input streams always need to block, output streams need to block when there is no sink.
+ // - When the sink exists, more sophisticated blocking algorithm is implemented by MonoPipe.
+ if (mCurrentRoute && !mIsInput && status != ::android::DEAD_OBJECT) return ::android::OK;
const long bufferDurationUs =
(*actualFrameCount) * MICROS_PER_SECOND / mContext.getSampleRate();
const auto totalDurationUs = (::android::uptimeNanos() - mStartTimeNs) / NANOS_PER_MICROSECOND;
@@ -163,6 +141,10 @@
}
::android::status_t StreamRemoteSubmix::refinePosition(StreamDescriptor::Position* position) {
+ if (!mCurrentRoute) {
+ RETURN_STATUS_IF_ERROR(setCurrentRoute());
+ if (!mCurrentRoute) return ::android::OK;
+ }
sp<MonoPipeReader> source = mCurrentRoute->getSource();
if (source == nullptr) {
return ::android::NO_INIT;
@@ -186,6 +168,7 @@
// Calculate the maximum size of the pipe buffer in frames for the specified stream.
size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() {
+ if (!mCurrentRoute) return r_submix::kDefaultPipeSizeInFrames;
auto pipeConfig = mCurrentRoute->getPipeConfig();
const size_t maxFrameSize = std::max(mStreamConfig.frameSize, pipeConfig.frameSize);
return (pipeConfig.frameCount * pipeConfig.frameSize) / maxFrameSize;
@@ -209,7 +192,7 @@
}
mWriteShutdownCount = 0;
- LOG(VERBOSE) << __func__ << ": " << mDeviceAddress.toString() << ", " << frameCount
+ LOG(VERBOSE) << __func__ << ": " << getDeviceAddress().toString() << ", " << frameCount
<< " frames";
const bool shouldBlockWrite = mCurrentRoute->shouldBlockWrite();
@@ -283,8 +266,9 @@
}
mReadErrorCount = 0;
- LOG(VERBOSE) << __func__ << ": " << mDeviceAddress.toString() << ", " << frameCount
+ LOG(VERBOSE) << __func__ << ": " << getDeviceAddress().toString() << ", " << frameCount
<< " frames";
+
// read the data from the pipe
char* buff = (char*)buffer;
size_t actuallyRead = 0;
@@ -324,10 +308,91 @@
return ::android::OK;
}
+::android::status_t StreamRemoteSubmix::setCurrentRoute() {
+ const auto address = getDeviceAddress();
+ if (address == AudioDeviceAddress{}) {
+ return ::android::OK;
+ }
+ mCurrentRoute = SubmixRoute::findOrCreateRoute(address, mStreamConfig);
+ if (mCurrentRoute == nullptr) {
+ return ::android::NO_INIT;
+ }
+ if (!mCurrentRoute->isStreamConfigValid(mIsInput, mStreamConfig)) {
+ LOG(ERROR) << __func__ << ": invalid stream config";
+ return ::android::NO_INIT;
+ }
+ sp<MonoPipe> sink = mCurrentRoute->getSink();
+ if (sink == nullptr) {
+ LOG(ERROR) << __func__ << ": nullptr sink when opening stream";
+ return ::android::NO_INIT;
+ }
+ if ((!mIsInput || mCurrentRoute->isStreamInOpen()) && sink->isShutdown()) {
+ LOG(DEBUG) << __func__ << ": Shut down sink when opening stream";
+ if (::android::OK != mCurrentRoute->resetPipe()) {
+ LOG(ERROR) << __func__ << ": reset pipe failed";
+ return ::android::NO_INIT;
+ }
+ }
+ mCurrentRoute->openStream(mIsInput);
+ return ::android::OK;
+}
+
+ndk::ScopedAStatus StreamRemoteSubmix::prepareToClose() {
+ if (!mIsInput) {
+ const auto address = getDeviceAddress();
+ if (address == AudioDeviceAddress{}) return ndk::ScopedAStatus::ok();
+ std::shared_ptr<SubmixRoute> route = SubmixRoute::findRoute(address);
+ if (route != nullptr) {
+ sp<MonoPipe> sink = route->getSink();
+ if (sink == nullptr) {
+ ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ LOG(DEBUG) << __func__ << ": shutting down MonoPipe sink";
+
+ sink->shutdown(true);
+ // The client already considers this stream as closed, release the output end.
+ route->closeStream(mIsInput);
+ } else {
+ LOG(DEBUG) << __func__ << ": stream already closed.";
+ ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus StreamRemoteSubmix::setConnectedDevices(const ConnectedDevices& devices) {
+ if (devices.size() > 1) {
+ LOG(ERROR) << __func__ << ": Only single device supported, got " << devices.size();
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+ AudioDeviceAddress newAddress;
+ if (!devices.empty()) {
+ if (auto deviceDesc = devices.front().type;
+ (mIsInput && deviceDesc.type != AudioDeviceType::IN_SUBMIX) ||
+ (!mIsInput && deviceDesc.type != AudioDeviceType::OUT_SUBMIX)) {
+ LOG(ERROR) << __func__ << ": Device type " << toString(deviceDesc.type)
+ << " not supported";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+ newAddress = devices.front().address;
+ LOG(DEBUG) << __func__ << ": connected to " << newAddress.toString();
+ } else {
+ LOG(DEBUG) << __func__ << ": disconnected";
+ }
+ RETURN_STATUS_IF_ERROR(StreamCommonImpl::setConnectedDevices(devices));
+ std::lock_guard guard(mLock);
+ if (mDeviceAddress != newAddress) {
+ mDeviceAddress = newAddress;
+ mDeviceAddressUpdated.store(true, std::memory_order_release);
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
StreamInRemoteSubmix::StreamInRemoteSubmix(StreamContext&& context,
const SinkMetadata& sinkMetadata,
const std::vector<MicrophoneInfo>& microphones)
- : StreamIn(std::move(context), microphones), StreamSwitcher(&mContextInstance, sinkMetadata) {}
+ : StreamIn(std::move(context), microphones),
+ StreamRemoteSubmix(&mContextInstance, sinkMetadata) {}
ndk::ScopedAStatus StreamInRemoteSubmix::getActiveMicrophones(
std::vector<MicrophoneDynamicInfo>* _aidl_return) {
@@ -336,66 +401,10 @@
return ndk::ScopedAStatus::ok();
}
-StreamSwitcher::DeviceSwitchBehavior StreamInRemoteSubmix::switchCurrentStream(
- const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
- // This implementation effectively postpones stream creation until
- // receiving the first call to 'setConnectedDevices' with a non-empty list.
- if (isStubStream()) {
- if (devices.size() == 1) {
- auto deviceDesc = devices.front().type;
- if (deviceDesc.type ==
- ::aidl::android::media::audio::common::AudioDeviceType::IN_SUBMIX) {
- return DeviceSwitchBehavior::CREATE_NEW_STREAM;
- }
- LOG(ERROR) << __func__ << ": Device type " << toString(deviceDesc.type)
- << " not supported";
- } else {
- LOG(ERROR) << __func__ << ": Only single device supported.";
- }
- return DeviceSwitchBehavior::UNSUPPORTED_DEVICES;
- }
- return DeviceSwitchBehavior::USE_CURRENT_STREAM;
-}
-
-std::unique_ptr<StreamCommonInterfaceEx> StreamInRemoteSubmix::createNewStream(
- const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices,
- StreamContext* context, const Metadata& metadata) {
- return std::unique_ptr<StreamCommonInterfaceEx>(
- new InnerStreamWrapper<StreamRemoteSubmix>(context, metadata, devices.front().address));
-}
-
StreamOutRemoteSubmix::StreamOutRemoteSubmix(StreamContext&& context,
const SourceMetadata& sourceMetadata,
const std::optional<AudioOffloadInfo>& offloadInfo)
: StreamOut(std::move(context), offloadInfo),
- StreamSwitcher(&mContextInstance, sourceMetadata) {}
-
-StreamSwitcher::DeviceSwitchBehavior StreamOutRemoteSubmix::switchCurrentStream(
- const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
- // This implementation effectively postpones stream creation until
- // receiving the first call to 'setConnectedDevices' with a non-empty list.
- if (isStubStream()) {
- if (devices.size() == 1) {
- auto deviceDesc = devices.front().type;
- if (deviceDesc.type ==
- ::aidl::android::media::audio::common::AudioDeviceType::OUT_SUBMIX) {
- return DeviceSwitchBehavior::CREATE_NEW_STREAM;
- }
- LOG(ERROR) << __func__ << ": Device type " << toString(deviceDesc.type)
- << " not supported";
- } else {
- LOG(ERROR) << __func__ << ": Only single device supported.";
- }
- return DeviceSwitchBehavior::UNSUPPORTED_DEVICES;
- }
- return DeviceSwitchBehavior::USE_CURRENT_STREAM;
-}
-
-std::unique_ptr<StreamCommonInterfaceEx> StreamOutRemoteSubmix::createNewStream(
- const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices,
- StreamContext* context, const Metadata& metadata) {
- return std::unique_ptr<StreamCommonInterfaceEx>(
- new InnerStreamWrapper<StreamRemoteSubmix>(context, metadata, devices.front().address));
-}
+ StreamRemoteSubmix(&mContextInstance, sourceMetadata) {}
} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/r_submix/SubmixRoute.h b/audio/aidl/default/r_submix/SubmixRoute.h
index 5425f12..0097f39 100644
--- a/audio/aidl/default/r_submix/SubmixRoute.h
+++ b/audio/aidl/default/r_submix/SubmixRoute.h
@@ -25,10 +25,12 @@
#include <media/nbaio/MonoPipe.h>
#include <media/nbaio/MonoPipeReader.h>
+#include <Utils.h>
#include <aidl/android/media/audio/common/AudioChannelLayout.h>
#include <aidl/android/media/audio/common/AudioDeviceAddress.h>
#include <aidl/android/media/audio/common/AudioFormatDescription.h>
+using aidl::android::hardware::audio::common::getFrameSizeInBytes;
using aidl::android::media::audio::common::AudioChannelLayout;
using aidl::android::media::audio::common::AudioFormatDescription;
using aidl::android::media::audio::common::AudioFormatType;
@@ -56,8 +58,8 @@
AudioChannelLayout channelLayout =
AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
AudioChannelLayout::LAYOUT_STEREO);
- size_t frameSize;
- size_t frameCount;
+ size_t frameSize = getFrameSizeInBytes(format, channelLayout);
+ size_t frameCount = 0;
};
class SubmixRoute {