From 29fc7492779de1b8ac69b9ef71ebe0d95d42e750 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Mon, 24 Jul 2023 11:44:50 -0700 Subject: [PATCH] audio: Add a helper class to simplify legacy HALs migration Since in the C and HIDL APIs the method for opening a stream receives the device to connect to, some legacy HAL implementations took an advantage of that. In AIDL APIs device port connection process is more dynamic and independent of the stream creation. To simplify porting of legacy implementations to AIDL, a helper class StreamSwitcher is added. It emulates the legacy behavior by allowing to postpone the stream implementation creation until the connected device is known. Until that moment, it exposes to the client a stub implementation of the stream interface. Bug: 264712385 Bug: 286914845 Test: atest VtsHalAudioCoreTargetTest Change-Id: Ie8ae0338fd22f705e00a34e56a7fa235eda5ed9e (cherry picked from commit 43a85cfb2b7931a8a7f4affd1324e448a31a848d) Merged-In: Ie8ae0338fd22f705e00a34e56a7fa235eda5ed9e --- audio/aidl/default/Android.bp | 1 + audio/aidl/default/Stream.cpp | 3 +- audio/aidl/default/StreamSwitcher.cpp | 230 ++++++++++++++++++ audio/aidl/default/include/core-impl/Stream.h | 22 +- .../include/core-impl/StreamRemoteSubmix.h | 4 +- .../default/include/core-impl/StreamStub.h | 4 +- .../include/core-impl/StreamSwitcher.h | 191 +++++++++++++++ .../default/include/core-impl/StreamUsb.h | 4 +- 8 files changed, 444 insertions(+), 15 deletions(-) create mode 100644 audio/aidl/default/StreamSwitcher.cpp create mode 100644 audio/aidl/default/include/core-impl/StreamSwitcher.h diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp index 19b2397de6..4e583a4808 100644 --- a/audio/aidl/default/Android.bp +++ b/audio/aidl/default/Android.bp @@ -76,6 +76,7 @@ cc_library { "ModulePrimary.cpp", "SoundDose.cpp", "Stream.cpp", + "StreamSwitcher.cpp", "Telephony.cpp", "alsa/Mixer.cpp", "alsa/ModuleAlsa.cpp", diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp index 740788288a..d2be48c389 100644 --- a/audio/aidl/default/Stream.cpp +++ b/audio/aidl/default/Stream.cpp @@ -670,8 +670,7 @@ ndk::ScopedAStatus StreamCommonImpl::close() { LOG(DEBUG) << __func__ << ": joining the worker thread..."; mWorker->stop(); LOG(DEBUG) << __func__ << ": worker thread joined"; - onClose(); - mWorker->setClosed(); + onClose(mWorker->setClosed()); return ndk::ScopedAStatus::ok(); } else { LOG(ERROR) << __func__ << ": stream was already closed"; diff --git a/audio/aidl/default/StreamSwitcher.cpp b/audio/aidl/default/StreamSwitcher.cpp new file mode 100644 index 0000000000..956f4138ba --- /dev/null +++ b/audio/aidl/default/StreamSwitcher.cpp @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#define LOG_TAG "AHAL_StreamSwitcher" + +#include +#include +#include + +#include "core-impl/StreamStub.h" +#include "core-impl/StreamSwitcher.h" + +using aidl::android::hardware::audio::effect::IEffect; +using aidl::android::media::audio::common::AudioDevice; + +namespace aidl::android::hardware::audio::core { + +StreamSwitcher::StreamSwitcher(StreamContext* context, const Metadata& metadata) + : mMetadata(metadata), mStream(new InnerStreamWrapper(context, mMetadata)) {} + +ndk::ScopedAStatus StreamSwitcher::closeCurrentStream(bool validateStreamState) { + if (!mStream) return ndk::ScopedAStatus::ok(); + RETURN_STATUS_IF_ERROR(mStream->prepareToClose()); + RETURN_STATUS_IF_ERROR(mStream->close()); + if (validateStreamState && !isValidClosingStreamState(mStream->getStatePriorToClosing())) { + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + mStream.reset(); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus StreamSwitcher::close() { + if (mStream != nullptr) { + auto status = closeCurrentStream(false /*validateStreamState*/); + // The actual state is irrelevant since only StreamSwitcher cares about it. + onClose(StreamDescriptor::State::STANDBY); + return status; + } + LOG(ERROR) << __func__ << ": stream was already closed"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); +} + +ndk::ScopedAStatus StreamSwitcher::prepareToClose() { + if (mStream != nullptr) { + return mStream->prepareToClose(); + } + LOG(ERROR) << __func__ << ": stream was closed"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); +} + +ndk::ScopedAStatus StreamSwitcher::updateHwAvSyncId(int32_t in_hwAvSyncId) { + if (mStream == nullptr) { + LOG(ERROR) << __func__ << ": stream was closed"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + RETURN_STATUS_IF_ERROR(mStream->updateHwAvSyncId(in_hwAvSyncId)); + mHwAvSyncId = in_hwAvSyncId; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus StreamSwitcher::getVendorParameters(const std::vector& in_ids, + std::vector* _aidl_return) { + if (mStream == nullptr) { + LOG(ERROR) << __func__ << ": stream was closed"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + if (mIsStubStream) { + LOG(ERROR) << __func__ << ": the stream is not connected"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + return mStream->getVendorParameters(in_ids, _aidl_return); +} + +ndk::ScopedAStatus StreamSwitcher::setVendorParameters( + const std::vector& in_parameters, bool in_async) { + if (mStream == nullptr) { + LOG(ERROR) << __func__ << ": stream was closed"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + if (mIsStubStream) { + mMissedParameters.emplace_back(in_parameters, in_async); + return ndk::ScopedAStatus::ok(); + } + return mStream->setVendorParameters(in_parameters, in_async); +} + +ndk::ScopedAStatus StreamSwitcher::addEffect(const std::shared_ptr& in_effect) { + if (mStream == nullptr) { + LOG(ERROR) << __func__ << ": stream was closed"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + if (!mIsStubStream) { + RETURN_STATUS_IF_ERROR(mStream->addEffect(in_effect)); + } + mEffects.push_back(in_effect); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus StreamSwitcher::removeEffect(const std::shared_ptr& in_effect) { + if (mStream == nullptr) { + LOG(ERROR) << __func__ << ": stream was closed"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + for (auto it = mEffects.begin(); it != mEffects.end();) { + if ((*it)->asBinder() == in_effect->asBinder()) { + it = mEffects.erase(it); + } else { + ++it; + } + } + return !mIsStubStream ? mStream->removeEffect(in_effect) : ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus StreamSwitcher::getStreamCommonCommon( + std::shared_ptr* _aidl_return) { + if (!mCommon) { + LOG(FATAL) << __func__ << ": the common interface was not created"; + } + *_aidl_return = mCommon.getInstance(); + LOG(DEBUG) << __func__ << ": returning " << _aidl_return->get()->asBinder().get(); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus StreamSwitcher::updateMetadataCommon(const Metadata& metadata) { + if (mStream == nullptr) { + LOG(ERROR) << __func__ << ": stream was closed"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + mMetadata = metadata; + return !mIsStubStream ? mStream->updateMetadataCommon(metadata) : ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus StreamSwitcher::initInstance( + const std::shared_ptr& delegate) { + mCommon = ndk::SharedRefBase::make(delegate); + // The delegate is null because StreamSwitcher handles IStreamCommon methods by itself. + return mStream->initInstance(nullptr); +} + +const StreamContext& StreamSwitcher::getContext() const { + return *mContext; +} + +bool StreamSwitcher::isClosed() const { + return mStream == nullptr || mStream->isClosed(); +} + +const StreamCommonInterface::ConnectedDevices& StreamSwitcher::getConnectedDevices() const { + return mStream->getConnectedDevices(); +} + +ndk::ScopedAStatus StreamSwitcher::setConnectedDevices(const std::vector& devices) { + LOG(DEBUG) << __func__ << ": " << ::android::internal::ToString(devices); + if (mStream->getConnectedDevices() == devices) return ndk::ScopedAStatus::ok(); + const DeviceSwitchBehavior behavior = switchCurrentStream(devices); + if (behavior == DeviceSwitchBehavior::UNSUPPORTED_DEVICES) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); + } else if (behavior == DeviceSwitchBehavior::SWITCH_TO_STUB_STREAM && !devices.empty()) { + // This is an error in the extending class. + LOG(FATAL) << __func__ + << ": switching to stub stream with connected devices is not allowed"; + } + if (behavior == USE_CURRENT_STREAM) { + mIsStubStream = false; + } else { + LOG(DEBUG) << __func__ << ": connected devices changed, switching stream"; + // Two streams can't be opened for the same context, thus we always need to close + // the current one before creating a new one. + RETURN_STATUS_IF_ERROR(closeCurrentStream(true /*validateStreamState*/)); + if (behavior == CREATE_NEW_STREAM) { + mStream = createNewStream(devices, mContext, mMetadata); + mIsStubStream = false; + } else { // SWITCH_TO_STUB_STREAM + mStream.reset(new InnerStreamWrapper(mContext, mMetadata)); + mIsStubStream = true; + } + // The delegate is null because StreamSwitcher handles IStreamCommon methods by itself. + if (ndk::ScopedAStatus status = mStream->initInstance(nullptr); !status.isOk()) { + // Need to close the current failed stream, and report an error. + // Since we can't operate without a stream implementation, put a stub in. + RETURN_STATUS_IF_ERROR(closeCurrentStream(false /*validateStreamState*/)); + mStream.reset(new InnerStreamWrapper(mContext, mMetadata)); + (void)mStream->initInstance(nullptr); + (void)mStream->setConnectedDevices(devices); + return status; + } + } + RETURN_STATUS_IF_ERROR(mStream->setConnectedDevices(devices)); + if (behavior == CREATE_NEW_STREAM) { + // These updates are less critical, only log warning on failure. + if (mHwAvSyncId.has_value()) { + if (auto status = mStream->updateHwAvSyncId(*mHwAvSyncId); !status.isOk()) { + LOG(WARNING) << __func__ << ": could not update HW AV Sync for a new stream: " + << status.getDescription(); + } + } + for (const auto& vndParam : mMissedParameters) { + if (auto status = mStream->setVendorParameters(vndParam.first, vndParam.second); + !status.isOk()) { + LOG(WARNING) << __func__ << ": error while setting parameters for a new stream: " + << status.getDescription(); + } + } + mMissedParameters.clear(); + for (const auto& effect : mEffects) { + if (auto status = mStream->addEffect(effect); !status.isOk()) { + LOG(WARNING) << __func__ << ": error while adding effect for a new stream: " + << status.getDescription(); + } + } + } + return ndk::ScopedAStatus::ok(); +} + +} // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h index 0970774263..fa2b760861 100644 --- a/audio/aidl/default/include/core-impl/Stream.h +++ b/audio/aidl/default/include/core-impl/Stream.h @@ -66,7 +66,8 @@ class StreamContext { DataMQ; // Ensure that this value is not used by any of StreamDescriptor.State enums - static constexpr int32_t STATE_CLOSED = -1; + static constexpr StreamDescriptor::State STATE_CLOSED = + static_cast(-1); struct DebugParameters { // An extra delay for transient states, in ms. @@ -205,10 +206,14 @@ struct DriverInterface { class StreamWorkerCommonLogic : public ::android::hardware::audio::common::StreamLogic { public: - bool isClosed() const { - return static_cast(mState.load()) == StreamContext::STATE_CLOSED; + bool isClosed() const { return mState == StreamContext::STATE_CLOSED; } + StreamDescriptor::State setClosed() { + auto prevState = mState.exchange(StreamContext::STATE_CLOSED); + if (prevState != StreamContext::STATE_CLOSED) { + mStatePriorToClosing = prevState; + } + return mStatePriorToClosing; } - void setClosed() { mState = static_cast(StreamContext::STATE_CLOSED); } void setIsConnected(bool connected) { mIsConnected = connected; } protected: @@ -231,6 +236,9 @@ class StreamWorkerCommonLogic : public ::android::hardware::audio::common::Strea // which happens on the worker thread only. StreamContext* const mContext; DriverInterface* const mDriver; + // This is the state the stream was in before being closed. It is retrieved by the main + // thread after joining the worker thread. + StreamDescriptor::State mStatePriorToClosing = StreamDescriptor::State::STANDBY; // Atomic fields are used both by the main and worker threads. std::atomic mIsConnected = false; static_assert(std::atomic::is_always_lock_free); @@ -252,7 +260,7 @@ struct StreamWorkerInterface { virtual ~StreamWorkerInterface() = default; virtual bool isClosed() const = 0; virtual void setIsConnected(bool isConnected) = 0; - virtual void setClosed() = 0; + virtual StreamDescriptor::State setClosed() = 0; virtual bool start() = 0; virtual void stop() = 0; }; @@ -267,7 +275,7 @@ class StreamWorkerImpl : public StreamWorkerInterface, : WorkerImpl(context, driver) {} bool isClosed() const override { return WorkerImpl::isClosed(); } void setIsConnected(bool isConnected) override { WorkerImpl::setIsConnected(isConnected); } - void setClosed() override { WorkerImpl::setClosed(); } + StreamDescriptor::State setClosed() override { return WorkerImpl::setClosed(); } bool start() override { return WorkerImpl::start(WorkerImpl::kThreadName, ANDROID_PRIORITY_AUDIO); } @@ -459,7 +467,7 @@ class StreamCommonImpl : virtual public StreamCommonInterface, virtual public Dr }; } - virtual void onClose() = 0; + virtual void onClose(StreamDescriptor::State statePriorToClosing) = 0; void stopWorker(); const StreamContext& mContext; diff --git a/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h b/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h index cc44a6e368..b39583ea8b 100644 --- a/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h +++ b/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h @@ -81,7 +81,7 @@ class StreamInRemoteSubmix final : public StreamIn, public StreamRemoteSubmix { const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones); private: - void onClose() override { defaultOnClose(); } + void onClose(StreamDescriptor::State) override { defaultOnClose(); } ndk::ScopedAStatus getActiveMicrophones( std::vector<::aidl::android::media::audio::common::MicrophoneDynamicInfo>* _aidl_return) override; @@ -97,7 +97,7 @@ class StreamOutRemoteSubmix final : public StreamOut, public StreamRemoteSubmix offloadInfo); private: - void onClose() override { defaultOnClose(); } + void onClose(StreamDescriptor::State) override { defaultOnClose(); } }; } // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/include/core-impl/StreamStub.h b/audio/aidl/default/include/core-impl/StreamStub.h index d16318107c..a8a3fc4961 100644 --- a/audio/aidl/default/include/core-impl/StreamStub.h +++ b/audio/aidl/default/include/core-impl/StreamStub.h @@ -52,7 +52,7 @@ class StreamInStub final : public StreamIn, public StreamStub { const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones); private: - void onClose() override { defaultOnClose(); } + void onClose(StreamDescriptor::State) override { defaultOnClose(); } }; class StreamOutStub final : public StreamOut, public StreamStub { @@ -64,7 +64,7 @@ class StreamOutStub final : public StreamOut, public StreamStub { offloadInfo); private: - void onClose() override { defaultOnClose(); } + void onClose(StreamDescriptor::State) override { defaultOnClose(); } }; } // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/include/core-impl/StreamSwitcher.h b/audio/aidl/default/include/core-impl/StreamSwitcher.h new file mode 100644 index 0000000000..e462481fc1 --- /dev/null +++ b/audio/aidl/default/include/core-impl/StreamSwitcher.h @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "Stream.h" + +namespace aidl::android::hardware::audio::core { + +// 'StreamSwitcher' is implementation of 'StreamCommonInterface' which allows +// dynamically switching the underlying stream implementation based on currently +// connected devices. This is achieved by replacing inheritance from +// 'StreamCommonImpl' with owning an instance of it. StreamSwitcher must be +// extended in order to supply the logic for choosing the stream +// implementation. When there are no connected devices, for instance, upon the +// creation, the StreamSwitcher engages an instance of a stub stream in order to +// keep serving requests coming via 'StreamDescriptor'. +// +// StreamSwitcher implements the 'IStreamCommon' interface directly, with +// necessary delegation to the current stream implementation. While the stub +// stream is engaged, any requests made via 'IStreamCommon' (parameters, effects +// setting, etc) are postponed and only delivered on device connection change +// to the "real" stream implementation provided by the extending class. This is why +// the behavior of StreamSwitcher in the "stub" state is not identical to behavior +// of 'StreamStub'. It can become a full substitute for 'StreamStub' once +// device connection change event occurs and the extending class returns +// 'LEAVE_CURRENT_STREAM' from 'switchCurrentStream' method. +// +// There is a natural limitation that the current stream implementation may only +// be switched when the stream is in the 'STANDBY' state. Thus, when the event +// to switch the stream occurs, the current stream is stopped and joined, and +// its last state is validated. Since the change of the set of connected devices +// normally occurs on patch updates, if the stream was not in standby, this is +// reported to the caller of 'IModule.setAudioPatch' as the 'EX_ILLEGAL_STATE' +// error. +// +// The simplest use case, when the implementor just needs to emulate the legacy HAL API +// behavior of receiving the connected devices upon stream creation, the implementation +// of the extending class can look as follows. We assume that 'StreamLegacy' implementation +// is the one requiring to know connected devices on creation: +// +// class StreamLegacy : public StreamCommonImpl { +// public: +// StreamLegacy(StreamContext* context, const Metadata& metadata, +// const std::vector& devices); +// }; +// +// class StreamOutLegacy final : public StreamOut, public StreamSwitcher { +// public: +// StreamOutLegacy(StreamContext&& context, metatadata etc.) +// private: +// DeviceSwitchBehavior switchCurrentStream(const std::vector&) override { +// // This implementation effectively postpones stream creation until +// // receiving the first call to 'setConnectedDevices' with a non-empty list. +// return isStubStream() ? DeviceSwitchBehavior::CREATE_NEW_STREAM : +// DeviceSwitchBehavior::USE_CURRENT_STREAM; +// } +// std::unique_ptr createNewStream( +// const std::vector& devices, +// StreamContext* context, const Metadata& metadata) override { +// return std::unique_ptr(new InnerStreamWrapper( +// context, metadata, devices)); +// } +// void onClose(StreamDescriptor::State) override { defaultOnClose(); } +// } +// + +class StreamCommonInterfaceEx : virtual public StreamCommonInterface { + public: + virtual StreamDescriptor::State getStatePriorToClosing() const = 0; +}; + +template +class InnerStreamWrapper : public T, public StreamCommonInterfaceEx { + public: + InnerStreamWrapper(StreamContext* context, const Metadata& metadata) : T(context, metadata) {} + StreamDescriptor::State getStatePriorToClosing() const override { return mStatePriorToClosing; } + + private: + // Do not need to do anything on close notification from the inner stream + // because StreamSwitcher handles IStreamCommon::close by itself. + void onClose(StreamDescriptor::State statePriorToClosing) override { + mStatePriorToClosing = statePriorToClosing; + } + + StreamDescriptor::State mStatePriorToClosing = StreamDescriptor::State::STANDBY; +}; + +class StreamSwitcher : virtual public StreamCommonInterface { + public: + StreamSwitcher(StreamContext* context, const Metadata& metadata); + + ndk::ScopedAStatus close() override; + ndk::ScopedAStatus prepareToClose() override; + ndk::ScopedAStatus updateHwAvSyncId(int32_t in_hwAvSyncId) override; + ndk::ScopedAStatus getVendorParameters(const std::vector& in_ids, + std::vector* _aidl_return) override; + ndk::ScopedAStatus setVendorParameters(const std::vector& in_parameters, + bool in_async) override; + ndk::ScopedAStatus addEffect( + const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect) + override; + ndk::ScopedAStatus removeEffect( + const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect) + override; + + ndk::ScopedAStatus getStreamCommonCommon(std::shared_ptr* _aidl_return) override; + ndk::ScopedAStatus updateMetadataCommon(const Metadata& metadata) override; + + ndk::ScopedAStatus initInstance( + const std::shared_ptr& delegate) override; + const StreamContext& getContext() const override; + bool isClosed() const override; + const ConnectedDevices& getConnectedDevices() const override; + ndk::ScopedAStatus setConnectedDevices( + const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) + override; + + protected: + // Since switching a stream requires closing down the current stream, StreamSwitcher + // asks the extending class its intent on the connected devices change. + enum DeviceSwitchBehavior { + // Continue using the current stream implementation. If it's the stub implementation, + // StreamSwitcher starts treating the stub stream as a "real" implementation, + // without effectively closing it and starting again. + USE_CURRENT_STREAM, + // This is the normal case when the extending class provides a "real" implementation + // which is not a stub implementation. + CREATE_NEW_STREAM, + // This is the case when the extending class wants to revert back to the initial + // condition of using a stub stream provided by the StreamSwitcher. This behavior + // is only allowed when the list of connected devices is empty. + SWITCH_TO_STUB_STREAM, + // Use when the set of devices is not supported by the extending class. This returns + // 'EX_UNSUPPORTED_OPERATION' from 'setConnectedDevices'. + UNSUPPORTED_DEVICES, + }; + // StreamSwitcher will call these methods from 'setConnectedDevices'. If the switch behavior + // is 'CREATE_NEW_STREAM', the 'createwNewStream' function will be called (with the same + // device vector) for obtaining a new stream implementation, assuming that closing + // the current stream was a success. + virtual DeviceSwitchBehavior switchCurrentStream( + const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) = 0; + virtual std::unique_ptr createNewStream( + const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices, + StreamContext* context, const Metadata& metadata) = 0; + virtual void onClose(StreamDescriptor::State streamPriorToClosing) = 0; + + bool isStubStream() const { return mIsStubStream; } + StreamCommonInterfaceEx* getCurrentStream() const { return mStream.get(); } + + private: + using VndParam = std::pair, bool /*isAsync*/>; + + static constexpr bool isValidClosingStreamState(StreamDescriptor::State state) { + return state == StreamDescriptor::State::STANDBY || state == StreamDescriptor::State::ERROR; + } + + ndk::ScopedAStatus closeCurrentStream(bool validateStreamState); + + // StreamSwitcher does not own the context. + StreamContext* mContext; + Metadata mMetadata; + ChildInterface mCommon; + // The current stream. + std::unique_ptr mStream; + // Indicates whether 'mCurrentStream' is a stub stream implementation + // maintained by StreamSwitcher until the extending class provides a "real" + // implementation. The invariant of this state is that there are no connected + // devices. + bool mIsStubStream = true; + // Storage for the data from commands received via 'IStreamCommon'. + std::optional mHwAvSyncId; + std::vector mMissedParameters; + std::vector> mEffects; +}; + +} // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/include/core-impl/StreamUsb.h b/audio/aidl/default/include/core-impl/StreamUsb.h index 4fcd0e48a2..74e30ff97b 100644 --- a/audio/aidl/default/include/core-impl/StreamUsb.h +++ b/audio/aidl/default/include/core-impl/StreamUsb.h @@ -53,7 +53,7 @@ class StreamInUsb final : public StreamIn, public StreamUsb { const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones); private: - void onClose() override { defaultOnClose(); } + void onClose(StreamDescriptor::State) override { defaultOnClose(); } ndk::ScopedAStatus getActiveMicrophones( std::vector<::aidl::android::media::audio::common::MicrophoneDynamicInfo>* _aidl_return) override; @@ -68,7 +68,7 @@ class StreamOutUsb final : public StreamOut, public StreamUsb { offloadInfo); private: - void onClose() override { defaultOnClose(); } + void onClose(StreamDescriptor::State) override { defaultOnClose(); } ndk::ScopedAStatus getHwVolume(std::vector* _aidl_return) override; ndk::ScopedAStatus setHwVolume(const std::vector& in_channelVolumes) override;