[go: nahoru, domu]

blob: 6b45866a38773a5de996a1645fffecba51179f66 [file] [log] [blame]
/*
* Copyright (C) 2022 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 <atomic>
#include <chrono>
#include <cstdlib>
#include <map>
#include <memory>
#include <optional>
#include <variant>
#include <StreamWorker.h>
#include <Utils.h>
#include <aidl/android/hardware/audio/common/SinkMetadata.h>
#include <aidl/android/hardware/audio/common/SourceMetadata.h>
#include <aidl/android/hardware/audio/core/BnStreamCommon.h>
#include <aidl/android/hardware/audio/core/BnStreamIn.h>
#include <aidl/android/hardware/audio/core/BnStreamOut.h>
#include <aidl/android/hardware/audio/core/IStreamCallback.h>
#include <aidl/android/hardware/audio/core/IStreamOutEventCallback.h>
#include <aidl/android/hardware/audio/core/StreamDescriptor.h>
#include <aidl/android/media/audio/common/AudioDevice.h>
#include <aidl/android/media/audio/common/AudioIoFlags.h>
#include <aidl/android/media/audio/common/AudioOffloadInfo.h>
#include <aidl/android/media/audio/common/MicrophoneInfo.h>
#include <error/expected_utils.h>
#include <fmq/AidlMessageQueue.h>
#include <system/thread_defs.h>
#include <utils/Errors.h>
#include "core-impl/ChildInterface.h"
#include "core-impl/SoundDose.h"
#include "core-impl/utils.h"
namespace aidl::android::hardware::audio::core {
// This class is similar to StreamDescriptor, but unlike
// the descriptor, it actually owns the objects implementing
// data exchange: FMQs etc, whereas StreamDescriptor only
// contains their descriptors.
class StreamContext {
public:
typedef ::android::AidlMessageQueue<
StreamDescriptor::Command,
::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
CommandMQ;
typedef ::android::AidlMessageQueue<
StreamDescriptor::Reply, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
ReplyMQ;
typedef ::android::AidlMessageQueue<
int8_t, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
DataMQ;
// Ensure that this value is not used by any of StreamDescriptor.State enums
static constexpr StreamDescriptor::State STATE_CLOSED =
static_cast<StreamDescriptor::State>(-1);
struct DebugParameters {
// An extra delay for transient states, in ms.
int transientStateDelayMs = 0;
// Force the "burst" command to move the SM to the TRANSFERRING state.
bool forceTransientBurst = false;
// Force the "drain" command to be synchronous, going directly to the IDLE state.
bool forceSynchronousDrain = false;
};
StreamContext() = default;
StreamContext(std::unique_ptr<CommandMQ> commandMQ, std::unique_ptr<ReplyMQ> replyMQ,
const ::aidl::android::media::audio::common::AudioFormatDescription& format,
const ::aidl::android::media::audio::common::AudioChannelLayout& channelLayout,
int sampleRate, const ::aidl::android::media::audio::common::AudioIoFlags& flags,
int32_t nominalLatencyMs, int32_t mixPortHandle, std::unique_ptr<DataMQ> dataMQ,
std::shared_ptr<IStreamCallback> asyncCallback,
std::shared_ptr<IStreamOutEventCallback> outEventCallback,
std::weak_ptr<sounddose::StreamDataProcessorInterface> streamDataProcessor,
DebugParameters debugParameters)
: mCommandMQ(std::move(commandMQ)),
mInternalCommandCookie(std::rand() | 1 /* make sure it's not 0 */),
mReplyMQ(std::move(replyMQ)),
mFormat(format),
mChannelLayout(channelLayout),
mSampleRate(sampleRate),
mFlags(flags),
mNominalLatencyMs(nominalLatencyMs),
mMixPortHandle(mixPortHandle),
mDataMQ(std::move(dataMQ)),
mAsyncCallback(asyncCallback),
mOutEventCallback(outEventCallback),
mStreamDataProcessor(streamDataProcessor),
mDebugParameters(debugParameters) {}
void fillDescriptor(StreamDescriptor* desc);
std::shared_ptr<IStreamCallback> getAsyncCallback() const { return mAsyncCallback; }
size_t getBufferSizeInFrames() const;
::aidl::android::media::audio::common::AudioChannelLayout getChannelLayout() const {
return mChannelLayout;
}
CommandMQ* getCommandMQ() const { return mCommandMQ.get(); }
DataMQ* getDataMQ() const { return mDataMQ.get(); }
::aidl::android::media::audio::common::AudioFormatDescription getFormat() const {
return mFormat;
}
::aidl::android::media::audio::common::AudioIoFlags getFlags() const { return mFlags; }
bool getForceTransientBurst() const { return mDebugParameters.forceTransientBurst; }
bool getForceSynchronousDrain() const { return mDebugParameters.forceSynchronousDrain; }
size_t getFrameSize() const;
int getInternalCommandCookie() const { return mInternalCommandCookie; }
int32_t getMixPortHandle() const { return mMixPortHandle; }
int32_t getNominalLatencyMs() const { return mNominalLatencyMs; }
std::shared_ptr<IStreamOutEventCallback> getOutEventCallback() const {
return mOutEventCallback;
}
std::weak_ptr<sounddose::StreamDataProcessorInterface> getStreamDataProcessor() const {
return mStreamDataProcessor;
}
void startStreamDataProcessor();
ReplyMQ* getReplyMQ() const { return mReplyMQ.get(); }
int getTransientStateDelayMs() const { return mDebugParameters.transientStateDelayMs; }
int getSampleRate() const { return mSampleRate; }
bool isValid() const;
// 'reset' is called on a Binder thread when closing the stream. Does not use
// locking because it only cleans MQ pointers which were also set on the Binder thread.
void reset();
// 'advanceFrameCount' and 'getFrameCount' are only called on the worker thread.
long advanceFrameCount(size_t increase) { return mFrameCount += increase; }
long getFrameCount() const { return mFrameCount; }
private:
// Fields are non const to allow move assignment.
std::unique_ptr<CommandMQ> mCommandMQ;
int mInternalCommandCookie; // The value used to confirm that the command was posted internally
std::unique_ptr<ReplyMQ> mReplyMQ;
::aidl::android::media::audio::common::AudioFormatDescription mFormat;
::aidl::android::media::audio::common::AudioChannelLayout mChannelLayout;
int mSampleRate;
::aidl::android::media::audio::common::AudioIoFlags mFlags;
int32_t mNominalLatencyMs;
int32_t mMixPortHandle;
std::unique_ptr<DataMQ> mDataMQ;
std::shared_ptr<IStreamCallback> mAsyncCallback;
std::shared_ptr<IStreamOutEventCallback> mOutEventCallback; // Only used by output streams
std::weak_ptr<sounddose::StreamDataProcessorInterface> mStreamDataProcessor;
DebugParameters mDebugParameters;
long mFrameCount = 0;
};
// This interface provides operations of the stream which are executed on the worker thread.
struct DriverInterface {
virtual ~DriverInterface() = default;
// All the methods below are called on the worker thread.
virtual ::android::status_t init() = 0; // This function is only called once.
virtual ::android::status_t drain(StreamDescriptor::DrainMode mode) = 0;
virtual ::android::status_t flush() = 0;
virtual ::android::status_t pause() = 0;
virtual ::android::status_t standby() = 0;
virtual ::android::status_t start() = 0;
virtual ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) = 0;
// No need to implement 'refinePosition' unless the driver can provide more precise
// data than just total frame count. For example, the driver may correctly account
// for any intermediate buffers.
virtual ::android::status_t refinePosition(StreamDescriptor::Position* /*position*/) {
return ::android::OK;
}
// Implement 'getMmapPositionAndLatency' is necessary if driver can support mmap stream.
virtual ::android::status_t getMmapPositionAndLatency(StreamDescriptor::Position* /*position*/,
int32_t* /*latency*/) {
return ::android::OK;
}
virtual void shutdown() = 0; // This function is only called once.
};
class StreamWorkerCommonLogic : public ::android::hardware::audio::common::StreamLogic {
public:
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 setIsConnected(bool connected) { mIsConnected = connected; }
protected:
using DataBufferElement = int8_t;
StreamWorkerCommonLogic(StreamContext* context, DriverInterface* driver)
: mContext(context),
mDriver(driver),
mTransientStateDelayMs(context->getTransientStateDelayMs()) {}
pid_t getTid() const;
std::string init() override;
void populateReply(StreamDescriptor::Reply* reply, bool isConnected) const;
void populateReplyWrongState(StreamDescriptor::Reply* reply,
const StreamDescriptor::Command& command) const;
void switchToTransientState(StreamDescriptor::State state) {
mState = state;
mTransientStateStart = std::chrono::steady_clock::now();
}
// The context is only used for reading, except for updating the frame count,
// 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);
std::atomic<StreamDescriptor::State> mState = StreamDescriptor::State::STANDBY;
// All fields below are used on the worker thread only.
const std::chrono::duration<int, std::milli> mTransientStateDelayMs;
std::chrono::time_point<std::chrono::steady_clock> mTransientStateStart;
// We use an array and the "size" field instead of a vector to be able to detect
// memory allocation issues.
std::unique_ptr<DataBufferElement[]> mDataBuffer;
size_t mDataBufferSize;
};
// This interface is used to decouple stream implementations from a concrete StreamWorker
// implementation.
struct StreamWorkerInterface {
using CreateInstance =
std::function<StreamWorkerInterface*(StreamContext* context, DriverInterface* driver)>;
virtual ~StreamWorkerInterface() = default;
virtual bool isClosed() const = 0;
virtual void setIsConnected(bool isConnected) = 0;
virtual StreamDescriptor::State setClosed() = 0;
virtual bool start() = 0;
virtual pid_t getTid() = 0;
virtual void stop() = 0;
virtual std::string getError() = 0;
};
template <class WorkerLogic>
class StreamWorkerImpl : public StreamWorkerInterface,
public ::android::hardware::audio::common::StreamWorker<WorkerLogic> {
using WorkerImpl = ::android::hardware::audio::common::StreamWorker<WorkerLogic>;
public:
StreamWorkerImpl(StreamContext* context, DriverInterface* driver)
: WorkerImpl(context, driver) {}
bool isClosed() const override { return WorkerImpl::isClosed(); }
void setIsConnected(bool isConnected) override { WorkerImpl::setIsConnected(isConnected); }
StreamDescriptor::State setClosed() override { return WorkerImpl::setClosed(); }
bool start() override {
// This is an "audio service thread," must have elevated priority.
return WorkerImpl::start(WorkerImpl::kThreadName, ANDROID_PRIORITY_URGENT_AUDIO);
}
pid_t getTid() override { return WorkerImpl::getTid(); }
void stop() override { return WorkerImpl::stop(); }
std::string getError() override { return WorkerImpl::getError(); }
};
class StreamInWorkerLogic : public StreamWorkerCommonLogic {
public:
static const std::string kThreadName;
StreamInWorkerLogic(StreamContext* context, DriverInterface* driver)
: StreamWorkerCommonLogic(context, driver) {}
protected:
Status cycle() override;
private:
bool read(size_t clientSize, StreamDescriptor::Reply* reply);
};
using StreamInWorker = StreamWorkerImpl<StreamInWorkerLogic>;
class StreamOutWorkerLogic : public StreamWorkerCommonLogic {
public:
static const std::string kThreadName;
StreamOutWorkerLogic(StreamContext* context, DriverInterface* driver)
: StreamWorkerCommonLogic(context, driver),
mEventCallback(context->getOutEventCallback()) {}
protected:
Status cycle() override;
private:
bool write(size_t clientSize, StreamDescriptor::Reply* reply);
std::shared_ptr<IStreamOutEventCallback> mEventCallback;
};
using StreamOutWorker = StreamWorkerImpl<StreamOutWorkerLogic>;
// This interface provides operations of the stream which are executed on a Binder pool thread.
// These methods originate both from the AIDL interface and its implementation.
struct StreamCommonInterface {
using ConnectedDevices = std::vector<::aidl::android::media::audio::common::AudioDevice>;
using Metadata =
std::variant<::aidl::android::hardware::audio::common::SinkMetadata /*IStreamIn*/,
::aidl::android::hardware::audio::common::SourceMetadata /*IStreamOut*/>;
static constexpr bool isInput(const Metadata& metadata) { return metadata.index() == 0; }
virtual ~StreamCommonInterface() = default;
// Methods below originate from the 'IStreamCommon' interface.
// This is semantically equivalent to inheriting from 'IStreamCommon' with a benefit
// that concrete stream implementations can inherit both from this interface and IStreamIn/Out.
virtual ndk::ScopedAStatus close() = 0;
virtual ndk::ScopedAStatus prepareToClose() = 0;
virtual ndk::ScopedAStatus updateHwAvSyncId(int32_t in_hwAvSyncId) = 0;
virtual ndk::ScopedAStatus getVendorParameters(const std::vector<std::string>& in_ids,
std::vector<VendorParameter>* _aidl_return) = 0;
virtual ndk::ScopedAStatus setVendorParameters(
const std::vector<VendorParameter>& in_parameters, bool in_async) = 0;
virtual ndk::ScopedAStatus addEffect(
const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>&
in_effect) = 0;
virtual ndk::ScopedAStatus removeEffect(
const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>&
in_effect) = 0;
// Methods below are common for both 'IStreamIn' and 'IStreamOut'. Note that
// 'updateMetadata' in them uses an individual structure which is wrapped here.
// The 'Common' suffix is added to distinguish them from the methods from 'IStreamIn/Out'.
virtual ndk::ScopedAStatus getStreamCommonCommon(
std::shared_ptr<IStreamCommon>* _aidl_return) = 0;
virtual ndk::ScopedAStatus updateMetadataCommon(const Metadata& metadata) = 0;
// Methods below are called by implementation of 'IModule', 'IStreamIn' and 'IStreamOut'.
virtual ndk::ScopedAStatus initInstance(
const std::shared_ptr<StreamCommonInterface>& delegate) = 0;
virtual const StreamContext& getContext() const = 0;
virtual bool isClosed() const = 0;
virtual const ConnectedDevices& getConnectedDevices() const = 0;
virtual ndk::ScopedAStatus setConnectedDevices(
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) = 0;
virtual ndk::ScopedAStatus bluetoothParametersUpdated() = 0;
};
// This is equivalent to automatically generated 'IStreamCommonDelegator' but uses
// a weak pointer to avoid creating a reference loop. The loop will occur because
// 'IStreamIn/Out.getStreamCommon' must return the same instance every time, thus
// the stream implementation must hold a strong pointer to an instance of 'IStreamCommon'.
// Also, we use 'StreamCommonInterface' here instead of 'IStreamCommon'.
class StreamCommonDelegator : public BnStreamCommon {
public:
explicit StreamCommonDelegator(const std::shared_ptr<StreamCommonInterface>& delegate)
: mDelegate(delegate) {}
private:
ndk::ScopedAStatus close() override {
auto delegate = mDelegate.lock();
return delegate != nullptr ? delegate->close()
: ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
ndk::ScopedAStatus prepareToClose() override {
auto delegate = mDelegate.lock();
return delegate != nullptr ? delegate->prepareToClose()
: ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
ndk::ScopedAStatus updateHwAvSyncId(int32_t in_hwAvSyncId) override {
auto delegate = mDelegate.lock();
return delegate != nullptr ? delegate->updateHwAvSyncId(in_hwAvSyncId)
: ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
ndk::ScopedAStatus getVendorParameters(const std::vector<std::string>& in_ids,
std::vector<VendorParameter>* _aidl_return) override {
auto delegate = mDelegate.lock();
return delegate != nullptr ? delegate->getVendorParameters(in_ids, _aidl_return)
: ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
ndk::ScopedAStatus setVendorParameters(const std::vector<VendorParameter>& in_parameters,
bool in_async) override {
auto delegate = mDelegate.lock();
return delegate != nullptr ? delegate->setVendorParameters(in_parameters, in_async)
: ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
ndk::ScopedAStatus addEffect(
const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect)
override {
auto delegate = mDelegate.lock();
return delegate != nullptr ? delegate->addEffect(in_effect)
: ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
ndk::ScopedAStatus removeEffect(
const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect)
override {
auto delegate = mDelegate.lock();
return delegate != nullptr ? delegate->removeEffect(in_effect)
: ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
// It is possible that on the client side the proxy for IStreamCommon will outlive
// the IStream* instance, and the server side IStream* instance will get destroyed
// while this IStreamCommon instance is still alive.
std::weak_ptr<StreamCommonInterface> mDelegate;
};
// The implementation of DriverInterface must be provided by each concrete stream implementation.
// Note that StreamCommonImpl does not own the context. This is to support swapping on the fly
// implementations of the stream while keeping the same IStreamIn/Out instance. It's that instance
// who must be owner of the context.
class StreamCommonImpl : virtual public StreamCommonInterface, virtual public DriverInterface {
public:
StreamCommonImpl(StreamContext* context, const Metadata& metadata,
const StreamWorkerInterface::CreateInstance& createWorker)
: mContext(*context), mMetadata(metadata), mWorker(createWorker(context, this)) {}
StreamCommonImpl(StreamContext* context, const Metadata& metadata)
: StreamCommonImpl(
context, metadata,
isInput(metadata) ? getDefaultInWorkerCreator() : getDefaultOutWorkerCreator()) {}
~StreamCommonImpl();
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 { return mContext; }
bool isClosed() const override { return mWorker->isClosed(); }
const ConnectedDevices& getConnectedDevices() const override { return mConnectedDevices; }
ndk::ScopedAStatus setConnectedDevices(
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices)
override;
ndk::ScopedAStatus bluetoothParametersUpdated() override;
protected:
static StreamWorkerInterface::CreateInstance getDefaultInWorkerCreator() {
return [](StreamContext* ctx, DriverInterface* driver) -> StreamWorkerInterface* {
return new StreamInWorker(ctx, driver);
};
}
static StreamWorkerInterface::CreateInstance getDefaultOutWorkerCreator() {
return [](StreamContext* ctx, DriverInterface* driver) -> StreamWorkerInterface* {
return new StreamOutWorker(ctx, driver);
};
}
virtual void onClose(StreamDescriptor::State statePriorToClosing) = 0;
void stopWorker();
const StreamContext& mContext;
Metadata mMetadata;
std::unique_ptr<StreamWorkerInterface> mWorker;
ChildInterface<StreamCommonDelegator> mCommon;
ConnectedDevices mConnectedDevices;
};
// Note: 'StreamIn/Out' can not be used on their own. Instead, they must be used for defining
// concrete input/output stream implementations.
class StreamIn : virtual public StreamCommonInterface, public BnStreamIn {
protected:
void defaultOnClose();
ndk::ScopedAStatus getStreamCommon(std::shared_ptr<IStreamCommon>* _aidl_return) override {
return getStreamCommonCommon(_aidl_return);
}
ndk::ScopedAStatus updateMetadata(const ::aidl::android::hardware::audio::common::SinkMetadata&
in_sinkMetadata) override {
return updateMetadataCommon(in_sinkMetadata);
}
ndk::ScopedAStatus getActiveMicrophones(
std::vector<::aidl::android::media::audio::common::MicrophoneDynamicInfo>* _aidl_return)
override;
ndk::ScopedAStatus getMicrophoneDirection(MicrophoneDirection* _aidl_return) override;
ndk::ScopedAStatus setMicrophoneDirection(MicrophoneDirection in_direction) override;
ndk::ScopedAStatus getMicrophoneFieldDimension(float* _aidl_return) override;
ndk::ScopedAStatus setMicrophoneFieldDimension(float in_zoom) override;
ndk::ScopedAStatus getHwGain(std::vector<float>* _aidl_return) override;
ndk::ScopedAStatus setHwGain(const std::vector<float>& in_channelGains) override;
friend class ndk::SharedRefBase;
StreamIn(StreamContext&& context,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones);
StreamContext mContextInstance;
const std::map<::aidl::android::media::audio::common::AudioDevice, std::string> mMicrophones;
};
class StreamInHwGainHelper {
protected:
explicit StreamInHwGainHelper(const StreamContext* context);
ndk::ScopedAStatus getHwGainImpl(std::vector<float>* _aidl_return);
ndk::ScopedAStatus setHwGainImpl(const std::vector<float>& in_channelGains);
const size_t mChannelCount;
std::vector<float> mHwGains;
};
class StreamOut : virtual public StreamCommonInterface, public BnStreamOut {
protected:
void defaultOnClose();
ndk::ScopedAStatus getStreamCommon(std::shared_ptr<IStreamCommon>* _aidl_return) override {
return getStreamCommonCommon(_aidl_return);
}
ndk::ScopedAStatus updateMetadata(
const ::aidl::android::hardware::audio::common::SourceMetadata& in_sourceMetadata)
override {
return updateMetadataCommon(in_sourceMetadata);
}
ndk::ScopedAStatus updateOffloadMetadata(
const ::aidl::android::hardware::audio::common::AudioOffloadMetadata&
in_offloadMetadata) override;
ndk::ScopedAStatus getHwVolume(std::vector<float>* _aidl_return) override;
ndk::ScopedAStatus setHwVolume(const std::vector<float>& in_channelVolumes) override;
ndk::ScopedAStatus getAudioDescriptionMixLevel(float* _aidl_return) override;
ndk::ScopedAStatus setAudioDescriptionMixLevel(float in_leveldB) override;
ndk::ScopedAStatus getDualMonoMode(
::aidl::android::media::audio::common::AudioDualMonoMode* _aidl_return) override;
ndk::ScopedAStatus setDualMonoMode(
::aidl::android::media::audio::common::AudioDualMonoMode in_mode) override;
ndk::ScopedAStatus getRecommendedLatencyModes(
std::vector<::aidl::android::media::audio::common::AudioLatencyMode>* _aidl_return)
override;
ndk::ScopedAStatus setLatencyMode(
::aidl::android::media::audio::common::AudioLatencyMode in_mode) override;
ndk::ScopedAStatus getPlaybackRateParameters(
::aidl::android::media::audio::common::AudioPlaybackRate* _aidl_return) override;
ndk::ScopedAStatus setPlaybackRateParameters(
const ::aidl::android::media::audio::common::AudioPlaybackRate& in_playbackRate)
override;
ndk::ScopedAStatus selectPresentation(int32_t in_presentationId, int32_t in_programId) override;
friend class ndk::SharedRefBase;
StreamOut(StreamContext&& context,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo);
StreamContext mContextInstance;
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo> mOffloadInfo;
std::optional<::aidl::android::hardware::audio::common::AudioOffloadMetadata> mOffloadMetadata;
};
class StreamOutHwVolumeHelper {
protected:
explicit StreamOutHwVolumeHelper(const StreamContext* context);
ndk::ScopedAStatus getHwVolumeImpl(std::vector<float>* _aidl_return);
ndk::ScopedAStatus setHwVolumeImpl(const std::vector<float>& in_channelVolumes);
const size_t mChannelCount;
std::vector<float> mHwVolumes;
};
// The recommended way to create a stream instance.
// 'StreamImpl' is the concrete stream implementation, 'StreamInOrOut' is either 'StreamIn' or
// 'StreamOut', the rest are the arguments forwarded to the constructor of 'StreamImpl'.
template <class StreamImpl, class StreamInOrOut, class... Args>
ndk::ScopedAStatus createStreamInstance(std::shared_ptr<StreamInOrOut>* result, Args&&... args) {
std::shared_ptr<StreamInOrOut> stream =
::ndk::SharedRefBase::make<StreamImpl>(std::forward<Args>(args)...);
RETURN_STATUS_IF_ERROR(stream->initInstance(stream));
*result = std::move(stream);
return ndk::ScopedAStatus::ok();
}
class StreamWrapper {
public:
explicit StreamWrapper(const std::shared_ptr<StreamIn>& streamIn)
: mStream(streamIn), mStreamBinder(streamIn->asBinder()) {}
explicit StreamWrapper(const std::shared_ptr<StreamOut>& streamOut)
: mStream(streamOut), mStreamBinder(streamOut->asBinder()) {}
ndk::SpAIBinder getBinder() const { return mStreamBinder; }
bool isStreamOpen() const {
auto s = mStream.lock();
return s && !s->isClosed();
}
ndk::ScopedAStatus setConnectedDevices(
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
auto s = mStream.lock();
if (s) return s->setConnectedDevices(devices);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus bluetoothParametersUpdated() {
auto s = mStream.lock();
if (s) return s->bluetoothParametersUpdated();
return ndk::ScopedAStatus::ok();
}
private:
std::weak_ptr<StreamCommonInterface> mStream;
ndk::SpAIBinder mStreamBinder;
};
class Streams {
public:
Streams() = default;
Streams(const Streams&) = delete;
Streams& operator=(const Streams&) = delete;
size_t count(int32_t id) {
// Streams do not remove themselves from the collection on close.
erase_if(mStreams, [](const auto& pair) { return !pair.second.isStreamOpen(); });
return mStreams.count(id);
}
void insert(int32_t portId, int32_t portConfigId, StreamWrapper sw) {
mStreams.insert(std::pair{portConfigId, sw});
mStreams.insert(std::pair{portId, std::move(sw)});
}
ndk::ScopedAStatus setStreamConnectedDevices(
int32_t portConfigId,
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
if (auto it = mStreams.find(portConfigId); it != mStreams.end()) {
return it->second.setConnectedDevices(devices);
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus bluetoothParametersUpdated() {
bool isOk = true;
for (auto& it : mStreams) {
if (!it.second.bluetoothParametersUpdated().isOk()) isOk = false;
}
return isOk ? ndk::ScopedAStatus::ok()
: ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
private:
// Maps port ids and port config ids to streams. Multimap because a port
// (not port config) can have multiple streams opened on it.
std::multimap<int32_t, StreamWrapper> mStreams;
};
} // namespace aidl::android::hardware::audio::core