mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 15:58:43 +00:00
audio: Add a helper class to simplify legacy HALs migration am: 43a85cfb2b
Original change: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/2674340 Change-Id: Id2572acb414548928dc3ee086d569d6882313254 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user