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
This commit is contained in:
parent
1eedc130e8
commit
43a85cfb2b
8 changed files with 444 additions and 15 deletions
|
@ -76,6 +76,7 @@ cc_library {
|
|||
"ModulePrimary.cpp",
|
||||
"SoundDose.cpp",
|
||||
"Stream.cpp",
|
||||
"StreamSwitcher.cpp",
|
||||
"Telephony.cpp",
|
||||
"alsa/Mixer.cpp",
|
||||
"alsa/ModuleAlsa.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";
|
||||
|
|
230
audio/aidl/default/StreamSwitcher.cpp
Normal file
230
audio/aidl/default/StreamSwitcher.cpp
Normal file
|
@ -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 <limits>
|
||||
|
||||
#define LOG_TAG "AHAL_StreamSwitcher"
|
||||
|
||||
#include <Utils.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <error/expected_utils.h>
|
||||
|
||||
#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<StreamStub>(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<std::string>& in_ids,
|
||||
std::vector<VendorParameter>* _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<VendorParameter>& 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<IEffect>& 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<IEffect>& 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<IStreamCommon>* _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<StreamCommonInterface>& delegate) {
|
||||
mCommon = ndk::SharedRefBase::make<StreamCommonDelegator>(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<AudioDevice>& 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<StreamStub>(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<StreamStub>(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
|
|
@ -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<StreamDescriptor::State>(-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<int32_t>(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<StreamDescriptor::State>(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<bool> mIsConnected = false;
|
||||
static_assert(std::atomic<StreamDescriptor::State>::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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
191
audio/aidl/default/include/core-impl/StreamSwitcher.h
Normal file
191
audio/aidl/default/include/core-impl/StreamSwitcher.h
Normal file
|
@ -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<AudioDevice>& devices);
|
||||
// };
|
||||
//
|
||||
// class StreamOutLegacy final : public StreamOut, public StreamSwitcher {
|
||||
// public:
|
||||
// StreamOutLegacy(StreamContext&& context, metatadata etc.)
|
||||
// private:
|
||||
// DeviceSwitchBehavior switchCurrentStream(const std::vector<AudioDevice>&) 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<StreamCommonInterfaceEx> createNewStream(
|
||||
// const std::vector<AudioDevice>& devices,
|
||||
// StreamContext* context, const Metadata& metadata) override {
|
||||
// return std::unique_ptr<StreamCommonInterfaceEx>(new InnerStreamWrapper<StreamLegacy>(
|
||||
// context, metadata, devices));
|
||||
// }
|
||||
// void onClose(StreamDescriptor::State) override { defaultOnClose(); }
|
||||
// }
|
||||
//
|
||||
|
||||
class StreamCommonInterfaceEx : virtual public StreamCommonInterface {
|
||||
public:
|
||||
virtual StreamDescriptor::State getStatePriorToClosing() const = 0;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
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<std::string>& in_ids,
|
||||
std::vector<VendorParameter>* _aidl_return) override;
|
||||
ndk::ScopedAStatus setVendorParameters(const std::vector<VendorParameter>& 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<IStreamCommon>* _aidl_return) override;
|
||||
ndk::ScopedAStatus updateMetadataCommon(const Metadata& metadata) override;
|
||||
|
||||
ndk::ScopedAStatus initInstance(
|
||||
const std::shared_ptr<StreamCommonInterface>& 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<StreamCommonInterfaceEx> 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<std::vector<VendorParameter>, 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<StreamCommonDelegator> mCommon;
|
||||
// The current stream.
|
||||
std::unique_ptr<StreamCommonInterfaceEx> 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<int32_t> mHwAvSyncId;
|
||||
std::vector<VndParam> mMissedParameters;
|
||||
std::vector<std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>> mEffects;
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
|
@ -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<float>* _aidl_return) override;
|
||||
ndk::ScopedAStatus setHwVolume(const std::vector<float>& in_channelVolumes) override;
|
||||
|
||||
|
|
Loading…
Reference in a new issue