audio: import aidl hal

* hardware/interfaces/audio/aidl/default/ at android-16.0.0_r1.
This commit is contained in:
Konsta
2025-10-22 17:31:20 +03:00
parent 3292156c2a
commit 7d6df38051
168 changed files with 27991 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
/*
* 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 <aidl/android/hardware/audio/core/SurroundSoundConfig.h>
#include <aidl/android/media/audio/common/AudioFormatDescription.h>
#include <android_audio_policy_configuration.h>
#include <media/AidlConversionUtil.h>
namespace aidl::android::hardware::audio::core::internal {
ConversionResult<::aidl::android::media::audio::common::AudioFormatDescription>
xsdc2aidl_AudioFormatDescription(const std::string& xsdc);
ConversionResult<SurroundSoundConfig> xsdc2aidl_SurroundSoundConfig(
const ::android::audio::policy::configuration::SurroundSound& xsdc);
} // namespace aidl::android::hardware::audio::core::internal

View File

@@ -0,0 +1,84 @@
/*
* 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 <memory>
#include <optional>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include <aidl/android/hardware/audio/core/SurroundSoundConfig.h>
#include <aidl/android/media/audio/common/AudioHalEngineConfig.h>
#include <android_audio_policy_configuration.h>
#include <android_audio_policy_configuration_enums.h>
#include <media/AidlConversionUtil.h>
#include "core-impl/Module.h"
#include "core-impl/XmlConverter.h"
namespace aidl::android::hardware::audio::core::internal {
class AudioPolicyConfigXmlConverter {
public:
using ModuleConfiguration = std::pair<std::string, std::unique_ptr<Module::Configuration>>;
using ModuleConfigs = std::vector<ModuleConfiguration>;
explicit AudioPolicyConfigXmlConverter(const std::string& configFilePath)
: mConverter(configFilePath, &::android::audio::policy::configuration::read) {
if (mConverter.getXsdcConfig()) {
init();
}
}
std::string getError() const { return mConverter.getError(); }
::android::status_t getStatus() const { return mConverter.getStatus(); }
const ::aidl::android::media::audio::common::AudioHalEngineConfig& getAidlEngineConfig();
const SurroundSoundConfig& getSurroundSoundConfig();
std::unique_ptr<ModuleConfigs> releaseModuleConfigs();
// Public for testing purposes.
static const SurroundSoundConfig& getDefaultSurroundSoundConfig();
private:
const std::optional<::android::audio::policy::configuration::AudioPolicyConfiguration>&
getXsdcConfig() const {
return mConverter.getXsdcConfig();
}
void addVolumeGroupstoEngineConfig();
void init();
void mapStreamToVolumeCurve(
const ::android::audio::policy::configuration::Volume& xsdcVolumeCurve);
void mapStreamsToVolumeCurves();
void parseVolumes();
ConversionResult<::aidl::android::media::audio::common::AudioHalVolumeCurve>
convertVolumeCurveToAidl(
const ::android::audio::policy::configuration::Volume& xsdcVolumeCurve);
::aidl::android::media::audio::common::AudioHalEngineConfig mAidlEngineConfig;
XmlConverter<::android::audio::policy::configuration::AudioPolicyConfiguration> mConverter;
std::unordered_map<std::string, ::android::audio::policy::configuration::Reference>
mVolumesReferenceMap;
std::unordered_map<::android::audio::policy::configuration::AudioStreamType,
std::vector<::aidl::android::media::audio::common::AudioHalVolumeCurve>>
mStreamToVolumeCurvesMap;
std::unique_ptr<ModuleConfigs> mModuleConfigurations = std::make_unique<ModuleConfigs>();
};
} // namespace aidl::android::hardware::audio::core::internal

View File

@@ -0,0 +1,76 @@
/*
* 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 <aidl/android/hardware/audio/core/BnBluetooth.h>
#include <aidl/android/hardware/audio/core/BnBluetoothA2dp.h>
#include <aidl/android/hardware/audio/core/BnBluetoothLe.h>
namespace aidl::android::hardware::audio::core {
class ParamChangeHandler {
public:
ParamChangeHandler() = default;
void registerHandler(std::function<ndk::ScopedAStatus()> handler) { mHandler = handler; }
protected:
std::function<ndk::ScopedAStatus()> mHandler = nullptr;
};
class Bluetooth : public BnBluetooth {
public:
Bluetooth();
private:
ndk::ScopedAStatus setScoConfig(const ScoConfig& in_config, ScoConfig* _aidl_return) override;
ndk::ScopedAStatus setHfpConfig(const HfpConfig& in_config, HfpConfig* _aidl_return) override;
ScoConfig mScoConfig;
HfpConfig mHfpConfig;
};
class BluetoothA2dp : public BnBluetoothA2dp, public ParamChangeHandler {
public:
BluetoothA2dp() = default;
ndk::ScopedAStatus isEnabled(bool* _aidl_return) override;
private:
ndk::ScopedAStatus setEnabled(bool in_enabled) override;
ndk::ScopedAStatus supportsOffloadReconfiguration(bool* _aidl_return) override;
ndk::ScopedAStatus reconfigureOffload(
const std::vector<::aidl::android::hardware::audio::core::VendorParameter>&
in_parameters) override;
bool mEnabled = false;
};
class BluetoothLe : public BnBluetoothLe, public ParamChangeHandler {
public:
BluetoothLe() = default;
ndk::ScopedAStatus isEnabled(bool* _aidl_return) override;
private:
ndk::ScopedAStatus setEnabled(bool in_enabled) override;
ndk::ScopedAStatus supportsOffloadReconfiguration(bool* _aidl_return) override;
ndk::ScopedAStatus reconfigureOffload(
const std::vector<::aidl::android::hardware::audio::core::VendorParameter>&
in_parameters) override;
bool mEnabled = false;
};
} // namespace aidl::android::hardware::audio::core

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2024 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 <aidl/android/hardware/audio/core/BnConfig.h>
#include <aidl/android/media/audio/common/AudioHalEngineConfig.h>
#include <system/audio_config.h>
#include <android_audio_policy_capengine_configuration.h>
#include <android_audio_policy_capengine_configuration_enums.h>
#include "EngineConfigXmlConverter.h"
namespace aidl::android::hardware::audio::core::internal {
namespace capconfiguration = ::android::audio::policy::capengine::configuration;
namespace aidlcommon = ::aidl::android::media::audio::common;
class CapEngineConfigXmlConverter {
public:
explicit CapEngineConfigXmlConverter(const std::string& configFilePath)
: mConverter(configFilePath, &capconfiguration::readConfigurableDomains) {
if (mConverter.getXsdcConfig()) {
init();
}
}
std::string getError() const { return mConverter.getError(); }
::android::status_t getStatus() const { return mConverter.getStatus(); }
std::optional<
std::vector<std::optional<::aidl::android::media::audio::common::AudioHalCapDomain>>>&
getAidlCapEngineConfig();
private:
ConversionResult<std::vector<aidlcommon::AudioHalCapParameter>> convertSettingToAidl(
const capconfiguration::SettingsType::Configuration& xsdcSetting);
ConversionResult<std::vector<aidlcommon::AudioHalCapConfiguration>> convertConfigurationsToAidl(
const std::vector<capconfiguration::ConfigurationsType>& xsdcConfigurationsVec,
const std::vector<capconfiguration::SettingsType>& xsdcSettingsVec);
ConversionResult<aidlcommon::AudioHalCapConfiguration> convertConfigurationToAidl(
const capconfiguration::ConfigurationsType::Configuration& xsdcConfiguration,
const capconfiguration::SettingsType::Configuration& xsdcSettingConfiguration);
ConversionResult<aidlcommon::AudioHalCapParameter> convertParamToAidl(
const capconfiguration::ConfigurableElementSettingsType& element);
ConversionResult<aidlcommon::AudioHalCapConfiguration> convertConfigurationToAidl(
const capconfiguration::ConfigurationsType::Configuration& xsdcConfiguration);
ConversionResult<aidlcommon::AudioHalCapDomain> convertConfigurableDomainToAidl(
const capconfiguration::ConfigurableDomainType& xsdcConfigurableDomain);
const std::optional<capconfiguration::ConfigurableDomains>& getXsdcConfig() {
return mConverter.getXsdcConfig();
}
void init();
std::optional<std::vector<std::optional<aidlcommon::AudioHalCapDomain>>> mAidlCapDomains;
XmlConverter<capconfiguration::ConfigurableDomains> mConverter;
};
} // namespace aidl::android::hardware::audio::core::internal

View File

@@ -0,0 +1,59 @@
/*
* 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 <memory>
#include <utility>
#include <android/binder_auto_utils.h>
#include <android/binder_ibinder_platform.h>
#include <system/thread_defs.h>
namespace aidl::android::hardware::audio::core {
// Helper used for interfaces that require a persistent instance. We hold them via a strong
// pointer. The binder token is retained for a call to 'setMinSchedulerPolicy'.
template <class C>
struct ChildInterface : private std::pair<std::shared_ptr<C>, ndk::SpAIBinder> {
ChildInterface() = default;
ChildInterface& operator=(const std::shared_ptr<C>& c) {
return operator=(std::shared_ptr<C>(c));
}
ChildInterface& operator=(std::shared_ptr<C>&& c) {
this->first = std::move(c);
return *this;
}
explicit operator bool() const { return !!this->first; }
C& operator*() const { return *(this->first); }
C* operator->() const { return this->first; }
std::shared_ptr<C> getPtr() { return this->first; }
// Use 'getInstance' when returning the interface instance.
std::shared_ptr<C> getInstance() {
(void)getBinder();
return this->first;
}
AIBinder* getBinder() {
if (this->second.get() == nullptr) {
const auto binder = this->second = this->first->asBinder();
AIBinder_setMinSchedulerPolicy(binder.get(), SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
AIBinder_setInheritRt(binder.get(), true);
}
return this->second.get();
}
};
} // namespace aidl::android::hardware::audio::core

View File

@@ -0,0 +1,43 @@
/*
* 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 <aidl/android/hardware/audio/core/BnConfig.h>
#include <system/audio_config.h>
#include "AudioPolicyConfigXmlConverter.h"
#include "EngineConfigXmlConverter.h"
namespace aidl::android::hardware::audio::core {
static const std::string kEngineConfigFileName = "audio_policy_engine_configuration.xml";
class Config : public BnConfig {
public:
explicit Config(internal::AudioPolicyConfigXmlConverter& apConverter)
: mAudioPolicyConverter(apConverter) {}
private:
ndk::ScopedAStatus getSurroundSoundConfig(SurroundSoundConfig* _aidl_return) override;
ndk::ScopedAStatus getEngineConfig(
aidl::android::media::audio::common::AudioHalEngineConfig* _aidl_return) override;
internal::AudioPolicyConfigXmlConverter& mAudioPolicyConverter;
internal::EngineConfigXmlConverter mEngConfigConverter{
::android::audio_find_readable_configuration_file(kEngineConfigFileName.c_str())};
};
} // namespace aidl::android::hardware::audio::core

View File

@@ -0,0 +1,29 @@
/*
* 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 <memory>
#include "Module.h"
namespace aidl::android::hardware::audio::core::internal {
std::unique_ptr<Module::Configuration> getConfiguration(Module::Type moduleType);
std::vector<aidl::android::media::audio::common::AudioProfile>
getStandard16And24BitPcmAudioProfiles();
} // namespace aidl::android::hardware::audio::core::internal

View File

@@ -0,0 +1,237 @@
/*
* Copyright 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 <condition_variable>
#include <mutex>
#include <android-base/thread_annotations.h>
#include <aidl/android/hardware/audio/common/SinkMetadata.h>
#include <aidl/android/hardware/audio/common/SourceMetadata.h>
#include <aidl/android/hardware/bluetooth/audio/BluetoothAudioStatus.h>
#include <aidl/android/hardware/bluetooth/audio/PcmConfiguration.h>
#include <aidl/android/hardware/bluetooth/audio/PresentationPosition.h>
#include <aidl/android/hardware/bluetooth/audio/SessionType.h>
#include <aidl/android/media/audio/common/AudioDeviceDescription.h>
namespace android::bluetooth::audio::aidl {
enum class BluetoothStreamState : uint8_t {
DISABLED = 0, // This stream is closing or Bluetooth profiles (A2DP/LE) is disabled
STANDBY,
STARTING,
STARTED,
SUSPENDING,
UNKNOWN,
};
std::ostream& operator<<(std::ostream& os, const BluetoothStreamState& state);
/**
* Proxy for Bluetooth Audio HW Module to communicate with Bluetooth Audio
* Session Control. All methods are not thread safe, so users must acquire a
* lock. Note: currently, getState() of DevicePortProxy is only used for
* verbose logging, it is not locked, so the state may not be synchronized.
*/
class BluetoothAudioPort {
public:
BluetoothAudioPort() = default;
virtual ~BluetoothAudioPort() = default;
/**
* Fetch output control / data path of BluetoothAudioPort and setup
* callbacks into BluetoothAudioProvider. If registerPort() returns false, the audio
* HAL must delete this BluetoothAudioPort and return EINVAL to caller
*/
virtual bool registerPort(
const ::aidl::android::media::audio::common::AudioDeviceDescription&) = 0;
/**
* Unregister this BluetoothAudioPort from BluetoothAudioSessionControl.
* Audio HAL must delete this BluetoothAudioPort after calling this.
*/
virtual void unregisterPort() = 0;
/**
* When the Audio framework / HAL tries to query audio config about format,
* channel mask and sample rate, it uses this function to fetch from the
* Bluetooth stack
*/
virtual bool loadAudioConfig(
::aidl::android::hardware::bluetooth::audio::PcmConfiguration&) = 0;
/**
* When the Audio framework / HAL wants to change the stream state, it invokes
* these 4 functions to control the Bluetooth stack (Audio Control Path).
* Note: standby(), start() and suspend() will return true when there are no errors.
* Called by Audio framework / HAL to change the state to stand by. When A2DP/LE profile is
* disabled, the port is first set to STANDBY by calling suspend and then mState is set to
* DISABLED. To reset the state back to STANDBY this method is called.
*/
virtual bool standby() = 0;
/**
* Called by Audio framework / HAL to start the stream
*/
virtual bool start() = 0;
/**
* Called by Audio framework / HAL to suspend the stream
*/
virtual bool suspend() = 0;
/**
* Called by Audio framework / HAL to stop the stream
*/
virtual void stop() = 0;
/**
* Called by the Audio framework / HAL to fetch information about audio frames
* presented to an external sink, or frames presented fror an internal sink
*/
virtual bool getPresentationPosition(
::aidl::android::hardware::bluetooth::audio::PresentationPosition&) const = 0;
/**
* Called by the Audio framework / HAL when the metadata of the stream's
* source has been changed.
*/
virtual bool updateSourceMetadata(
const ::aidl::android::hardware::audio::common::SourceMetadata&) const {
return false;
}
/**
* Called by the Audio framework / HAL when the metadata of the stream's
* sink has been changed.
*/
virtual bool updateSinkMetadata(
const ::aidl::android::hardware::audio::common::SinkMetadata&) const {
return false;
}
/**
* Return the current BluetoothStreamState
*/
virtual BluetoothStreamState getState() const = 0;
/**
* Set the current BluetoothStreamState
*/
virtual bool setState(BluetoothStreamState) = 0;
virtual bool isA2dp() const = 0;
virtual bool isLeAudio() const = 0;
virtual bool getPreferredDataIntervalUs(size_t&) const = 0;
virtual size_t writeData(const void*, size_t) const { return 0; }
virtual size_t readData(void*, size_t) const { return 0; }
};
class BluetoothAudioPortAidl : public BluetoothAudioPort {
public:
BluetoothAudioPortAidl();
virtual ~BluetoothAudioPortAidl();
bool registerPort(const ::aidl::android::media::audio::common::AudioDeviceDescription&
description) override;
void unregisterPort() override;
bool loadAudioConfig(
::aidl::android::hardware::bluetooth::audio::PcmConfiguration& audio_cfg) override;
bool standby() override;
bool start() override;
bool suspend() override;
void stop() override;
bool getPresentationPosition(::aidl::android::hardware::bluetooth::audio::PresentationPosition&
presentation_position) const override;
bool updateSourceMetadata(const ::aidl::android::hardware::audio::common::SourceMetadata&
sourceMetadata) const override;
bool updateSinkMetadata(const ::aidl::android::hardware::audio::common::SinkMetadata&
sinkMetadata) const override;
/**
* Return the current BluetoothStreamState
* Note: This method is used for logging, does not lock, so value returned may not be latest
*/
BluetoothStreamState getState() const override NO_THREAD_SAFETY_ANALYSIS;
bool setState(BluetoothStreamState state) override;
bool isA2dp() const override;
bool isLeAudio() const override;
bool getPreferredDataIntervalUs(size_t& interval_us) const override;
protected:
uint16_t mCookie;
BluetoothStreamState mState GUARDED_BY(mCvMutex);
::aidl::android::hardware::bluetooth::audio::SessionType mSessionType;
// WR to support Mono: True if fetching Stereo and mixing into Mono
bool mIsStereoToMono = false;
bool inUse() const;
std::string debugMessage() const;
private:
// start()/suspend() report state change status via callback. Wait until kMaxWaitingTimeMs or a
// state change after a call to start()/suspend() and analyse the returned status. Below mutex,
// conditional variable serves this purpose.
mutable std::mutex mCvMutex;
std::condition_variable mInternalCv GUARDED_BY(mCvMutex);
// Check and initialize session type for |devices| If failed, this
// BluetoothAudioPortAidl is not initialized and must be deleted.
bool initSessionType(
const ::aidl::android::media::audio::common::AudioDeviceDescription& description);
bool condWaitState(BluetoothStreamState state);
void controlResultHandler(
uint16_t cookie,
const ::aidl::android::hardware::bluetooth::audio::BluetoothAudioStatus& status);
void sessionChangedHandler(uint16_t cookie);
};
class BluetoothAudioPortAidlOut : public BluetoothAudioPortAidl {
public:
bool loadAudioConfig(
::aidl::android::hardware::bluetooth::audio::PcmConfiguration& audio_cfg) override;
// The audio data path to the Bluetooth stack (Software encoding)
size_t writeData(const void* buffer, size_t bytes) const override;
};
class BluetoothAudioPortAidlIn : public BluetoothAudioPortAidl {
public:
// The audio data path from the Bluetooth stack (Software decoded)
size_t readData(void* buffer, size_t bytes) const override;
};
} // namespace android::bluetooth::audio::aidl

View File

@@ -0,0 +1,53 @@
/*
* Copyright (C) 2024 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 "core-impl/Stream.h"
namespace aidl::android::hardware::audio::core {
class DriverStubImpl : virtual public DriverInterface {
public:
explicit DriverStubImpl(const StreamContext& context)
: DriverStubImpl(context, 500 /*asyncSleepTimeUs*/) {}
DriverStubImpl(const StreamContext& context, int asyncSleepTimeUs);
::android::status_t init(DriverCallbackInterface* callback) override;
::android::status_t drain(StreamDescriptor::DrainMode) override;
::android::status_t flush() override;
::android::status_t pause() override;
::android::status_t standby() override;
::android::status_t start() override;
::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) override;
void shutdown() override;
protected:
const size_t mBufferSizeFrames;
const size_t mFrameSizeBytes;
const int mSampleRate;
const bool mIsAsynchronous;
const bool mIsInput;
const int32_t mMixPortHandle;
const int mAsyncSleepTimeUs;
bool mIsInitialized = false; // Used for validating the state machine logic.
bool mIsStandby = true; // Used for validating the state machine logic.
int64_t mStartTimeNs = 0;
long mFramesSinceStart = 0;
};
} // namespace aidl::android::hardware::audio::core

View File

@@ -0,0 +1,86 @@
/*
* 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 <string>
#include <unordered_map>
#include <android_audio_policy_engine_configuration.h>
#include <android_audio_policy_engine_configuration_enums.h>
#include <media/AidlConversionUtil.h>
#include "core-impl/XmlConverter.h"
namespace aidl::android::hardware::audio::core::internal {
class EngineConfigXmlConverter {
public:
explicit EngineConfigXmlConverter(const std::string& configFilePath)
: mConverter(configFilePath, &::android::audio::policy::engine::configuration::read) {
if (mConverter.getXsdcConfig()) {
init();
}
}
std::string getError() const { return mConverter.getError(); }
::android::status_t getStatus() const { return mConverter.getStatus(); }
::aidl::android::media::audio::common::AudioHalEngineConfig& getAidlEngineConfig();
private:
const std::optional<::android::audio::policy::engine::configuration::Configuration>&
getXsdcConfig() {
return mConverter.getXsdcConfig();
}
void init();
void initProductStrategyMap();
ConversionResult<::aidl::android::media::audio::common::AudioAttributes>
convertAudioAttributesToAidl(
const ::android::audio::policy::engine::configuration::AttributesType&
xsdcAudioAttributes);
ConversionResult<::aidl::android::media::audio::common::AudioHalAttributesGroup>
convertAttributesGroupToAidl(
const ::android::audio::policy::engine::configuration::AttributesGroup&
xsdcAttributesGroup);
ConversionResult<::aidl::android::media::audio::common::AudioHalProductStrategy>
convertProductStrategyToAidl(const ::android::audio::policy::engine::configuration::
ProductStrategies::ProductStrategy& xsdcProductStrategy);
ConversionResult<int> convertProductStrategyIdToAidl(int xsdcId);
ConversionResult<int> convertProductStrategyNameToAidl(
const std::string& xsdcProductStrategyName);
ConversionResult<::aidl::android::media::audio::common::AudioHalVolumeCurve>
convertVolumeCurveToAidl(
const ::android::audio::policy::engine::configuration::Volume& xsdcVolumeCurve);
ConversionResult<::aidl::android::media::audio::common::AudioHalVolumeGroup>
convertVolumeGroupToAidl(
const ::android::audio::policy::engine::configuration::VolumeGroupsType::VolumeGroup&
xsdcVolumeGroup);
::aidl::android::media::audio::common::AudioHalEngineConfig mAidlEngineConfig;
XmlConverter<::android::audio::policy::engine::configuration::Configuration> mConverter;
std::unordered_map<std::string,
::android::audio::policy::engine::configuration::AttributesRefType>
mAttributesReferenceMap;
std::unordered_map<std::string, ::android::audio::policy::engine::configuration::VolumeRef>
mVolumesReferenceMap;
std::unordered_map<std::string, int> mProductStrategyMap;
int mNextVendorStrategy = ::aidl::android::media::audio::common::AudioHalProductStrategy::
VENDOR_STRATEGY_ID_START;
std::optional<int> mDefaultProductStrategyId;
};
} // namespace aidl::android::hardware::audio::core::internal

View File

@@ -0,0 +1,281 @@
/*
* 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 <functional>
#include <iostream>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <Utils.h>
#include <aidl/android/hardware/audio/core/BnModule.h>
#include "core-impl/ChildInterface.h"
#include "core-impl/Stream.h"
namespace aidl::android::hardware::audio::core {
class Module : public BnModule {
public:
struct Configuration {
std::vector<::aidl::android::media::audio::common::AudioPort> ports;
std::vector<::aidl::android::media::audio::common::AudioPortConfig> portConfigs;
std::vector<::aidl::android::media::audio::common::AudioPortConfig> initialConfigs;
// Port id -> List of profiles to use when the device port state is set to 'connected'
// in connection simulation mode.
std::map<int32_t, std::vector<::aidl::android::media::audio::common::AudioProfile>>
connectedProfiles;
std::vector<AudioRoute> routes;
std::vector<AudioPatch> patches;
int32_t nextPortId = 1;
int32_t nextPatchId = 1;
};
enum Type : int { DEFAULT, R_SUBMIX, STUB, USB, BLUETOOTH };
static std::shared_ptr<Module> createInstance(Type type) {
return createInstance(type, std::make_unique<Configuration>());
}
static std::shared_ptr<Module> createInstance(Type type,
std::unique_ptr<Configuration>&& config);
static std::optional<Type> typeFromString(const std::string& type);
Module(Type type, std::unique_ptr<Configuration>&& config);
protected:
// The vendor extension done via inheritance can override interface methods and augment
// a call to the base implementation.
binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
ndk::ScopedAStatus setModuleDebug(
const ::aidl::android::hardware::audio::core::ModuleDebug& in_debug) override;
ndk::ScopedAStatus getTelephony(std::shared_ptr<ITelephony>* _aidl_return) override;
ndk::ScopedAStatus getBluetooth(std::shared_ptr<IBluetooth>* _aidl_return) override;
ndk::ScopedAStatus getBluetoothA2dp(std::shared_ptr<IBluetoothA2dp>* _aidl_return) override;
ndk::ScopedAStatus getBluetoothLe(std::shared_ptr<IBluetoothLe>* _aidl_return) override;
ndk::ScopedAStatus connectExternalDevice(
const ::aidl::android::media::audio::common::AudioPort& in_templateIdAndAdditionalData,
::aidl::android::media::audio::common::AudioPort* _aidl_return) override;
ndk::ScopedAStatus disconnectExternalDevice(int32_t in_portId) override;
ndk::ScopedAStatus prepareToDisconnectExternalDevice(int32_t in_portId) override;
ndk::ScopedAStatus getAudioPatches(std::vector<AudioPatch>* _aidl_return) override;
ndk::ScopedAStatus getAudioPort(
int32_t in_portId,
::aidl::android::media::audio::common::AudioPort* _aidl_return) override;
ndk::ScopedAStatus getAudioPortConfigs(
std::vector<::aidl::android::media::audio::common::AudioPortConfig>* _aidl_return)
override;
ndk::ScopedAStatus getAudioPorts(
std::vector<::aidl::android::media::audio::common::AudioPort>* _aidl_return) override;
ndk::ScopedAStatus getAudioRoutes(std::vector<AudioRoute>* _aidl_return) override;
ndk::ScopedAStatus getAudioRoutesForAudioPort(
int32_t in_portId,
std::vector<::aidl::android::hardware::audio::core::AudioRoute>* _aidl_return) override;
ndk::ScopedAStatus openInputStream(
const ::aidl::android::hardware::audio::core::IModule::OpenInputStreamArguments&
in_args,
::aidl::android::hardware::audio::core::IModule::OpenInputStreamReturn* _aidl_return)
override;
ndk::ScopedAStatus openOutputStream(
const ::aidl::android::hardware::audio::core::IModule::OpenOutputStreamArguments&
in_args,
::aidl::android::hardware::audio::core::IModule::OpenOutputStreamReturn* _aidl_return)
override;
ndk::ScopedAStatus getSupportedPlaybackRateFactors(
SupportedPlaybackRateFactors* _aidl_return) override;
ndk::ScopedAStatus setAudioPatch(const AudioPatch& in_requested,
AudioPatch* _aidl_return) override;
ndk::ScopedAStatus setAudioPortConfig(
const ::aidl::android::media::audio::common::AudioPortConfig& in_requested,
::aidl::android::media::audio::common::AudioPortConfig* out_suggested,
bool* _aidl_return) override;
ndk::ScopedAStatus resetAudioPatch(int32_t in_patchId) override;
ndk::ScopedAStatus resetAudioPortConfig(int32_t in_portConfigId) override;
ndk::ScopedAStatus getMasterMute(bool* _aidl_return) override;
ndk::ScopedAStatus setMasterMute(bool in_mute) override;
ndk::ScopedAStatus getMasterVolume(float* _aidl_return) override;
ndk::ScopedAStatus setMasterVolume(float in_volume) override;
ndk::ScopedAStatus getMicMute(bool* _aidl_return) override;
ndk::ScopedAStatus setMicMute(bool in_mute) override;
ndk::ScopedAStatus getMicrophones(
std::vector<::aidl::android::media::audio::common::MicrophoneInfo>* _aidl_return)
override;
ndk::ScopedAStatus updateAudioMode(
::aidl::android::media::audio::common::AudioMode in_mode) override;
ndk::ScopedAStatus updateScreenRotation(
::aidl::android::hardware::audio::core::IModule::ScreenRotation in_rotation) override;
ndk::ScopedAStatus updateScreenState(bool in_isTurnedOn) override;
ndk::ScopedAStatus getSoundDose(std::shared_ptr<sounddose::ISoundDose>* _aidl_return) override;
ndk::ScopedAStatus generateHwAvSyncId(int32_t* _aidl_return) 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 addDeviceEffect(
int32_t in_portConfigId,
const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect)
override;
ndk::ScopedAStatus removeDeviceEffect(
int32_t in_portConfigId,
const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect)
override;
ndk::ScopedAStatus getMmapPolicyInfos(
::aidl::android::media::audio::common::AudioMMapPolicyType mmapPolicyType,
std::vector<::aidl::android::media::audio::common::AudioMMapPolicyInfo>* _aidl_return)
override;
ndk::ScopedAStatus supportsVariableLatency(bool* _aidl_return) override;
ndk::ScopedAStatus getAAudioMixerBurstCount(int32_t* _aidl_return) override;
ndk::ScopedAStatus getAAudioHardwareBurstMinUsec(int32_t* _aidl_return) override;
// The maximum stream buffer size is 1 GiB = 2 ** 30 bytes;
static constexpr int32_t kMaximumStreamBufferSizeBytes = 1 << 30;
private:
struct VendorDebug {
static const std::string kForceTransientBurstName;
static const std::string kForceSynchronousDrainName;
bool forceTransientBurst = false;
bool forceSynchronousDrain = false;
};
// ids of device ports created at runtime via 'connectExternalDevice'.
// Also stores a list of ids of mix ports with dynamic profiles that were populated from
// the connected port. This list can be empty, thus an int->int multimap can't be used.
using ConnectedDevicePorts = std::map<int32_t, std::set<int32_t>>;
// Maps port ids and port config ids to patch ids.
// Multimap because both ports and configs can be used by multiple patches.
using Patches = std::multimap<int32_t, int32_t>;
static const std::string kClipTransitionSupportName;
const Type mType;
std::unique_ptr<Configuration> mConfig;
ModuleDebug mDebug;
VendorDebug mVendorDebug;
ConnectedDevicePorts mConnectedDevicePorts;
Streams mStreams;
Patches mPatches;
bool mMicMute = false;
bool mMasterMute = false;
float mMasterVolume = 1.0f;
ChildInterface<sounddose::SoundDose> mSoundDose;
std::optional<bool> mIsMmapSupported;
protected:
// The following virtual functions are intended for vendor extension via inheritance.
virtual ndk::ScopedAStatus createInputStream(
StreamContext&& context,
const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
std::shared_ptr<StreamIn>* result) = 0;
virtual ndk::ScopedAStatus createOutputStream(
StreamContext&& context,
const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo,
std::shared_ptr<StreamOut>* result) = 0;
// If the module is unable to populate the connected device port correctly, the returned error
// code must correspond to the errors of `IModule.connectedExternalDevice` method.
virtual ndk::ScopedAStatus populateConnectedDevicePort(
::aidl::android::media::audio::common::AudioPort* audioPort, int32_t nextPortId);
// If the module finds that the patch endpoints configurations are not matched, the returned
// error code must correspond to the errors of `IModule.setAudioPatch` method.
virtual ndk::ScopedAStatus checkAudioPatchEndpointsMatch(
const std::vector<::aidl::android::media::audio::common::AudioPortConfig*>& sources,
const std::vector<::aidl::android::media::audio::common::AudioPortConfig*>& sinks);
virtual void onExternalDeviceConnectionChanged(
const ::aidl::android::media::audio::common::AudioPort& audioPort, bool connected);
virtual void onPrepareToDisconnectExternalDevice(
const ::aidl::android::media::audio::common::AudioPort& audioPort);
virtual ndk::ScopedAStatus onMasterMuteChanged(bool mute);
virtual ndk::ScopedAStatus onMasterVolumeChanged(float volume);
virtual std::vector<::aidl::android::media::audio::common::MicrophoneInfo> getMicrophoneInfos();
virtual std::unique_ptr<Configuration> initializeConfig();
virtual int32_t getNominalLatencyMs(
const ::aidl::android::media::audio::common::AudioPortConfig& portConfig);
virtual ndk::ScopedAStatus calculateBufferSizeFrames(
const ::aidl::android::media::audio::common::AudioFormatDescription &format,
int32_t latencyMs, int32_t sampleRateHz, int32_t *bufferSizeFrames);
virtual ndk::ScopedAStatus createMmapBuffer(
const ::aidl::android::media::audio::common::AudioPortConfig& portConfig,
int32_t bufferSizeFrames, int32_t frameSizeBytes, MmapBufferDescriptor* desc);
// Utility and helper functions accessible to subclasses.
static int32_t calculateBufferSizeFramesForPcm(int32_t latencyMs, int32_t sampleRateHz) {
const int32_t rawSizeFrames =
aidl::android::hardware::audio::common::frameCountFromDurationMs(latencyMs,
sampleRateHz);
// Round up to nearest 16 frames since in the framework this is the size of a mixer burst.
const int32_t multipleOf16 = (rawSizeFrames + 15) & ~15;
if (sampleRateHz < 44100 || multipleOf16 <= 512) return multipleOf16;
// Larger buffers should use powers of 2.
int32_t powerOf2 = 1;
while (powerOf2 < multipleOf16) powerOf2 <<= 1;
return powerOf2;
}
ndk::ScopedAStatus bluetoothParametersUpdated();
void cleanUpPatch(int32_t patchId);
ndk::ScopedAStatus createStreamContext(
int32_t in_portConfigId, int64_t in_bufferSizeFrames,
std::shared_ptr<IStreamCallback> asyncCallback,
std::shared_ptr<IStreamOutEventCallback> outEventCallback,
::aidl::android::hardware::audio::core::StreamContext* out_context);
std::vector<::aidl::android::media::audio::common::AudioDevice> findConnectedDevices(
int32_t portConfigId);
std::set<int32_t> findConnectedPortConfigIds(int32_t portConfigId);
ndk::ScopedAStatus findPortIdForNewStream(
int32_t in_portConfigId, ::aidl::android::media::audio::common::AudioPort** port);
// Note: does not assign an ID to the config.
bool generateDefaultPortConfig(const ::aidl::android::media::audio::common::AudioPort& port,
::aidl::android::media::audio::common::AudioPortConfig* config);
std::vector<AudioRoute*> getAudioRoutesForAudioPortImpl(int32_t portId);
Configuration& getConfig();
const ConnectedDevicePorts& getConnectedDevicePorts() const { return mConnectedDevicePorts; }
std::vector<::aidl::android::media::audio::common::AudioDevice>
getDevicesFromDevicePortConfigIds(const std::set<int32_t>& devicePortConfigIds);
bool getMasterMute() const { return mMasterMute; }
bool getMasterVolume() const { return mMasterVolume; }
bool getMicMute() const { return mMicMute; }
const ModuleDebug& getModuleDebug() const { return mDebug; }
const Patches& getPatches() const { return mPatches; }
std::set<int32_t> getRoutableAudioPortIds(int32_t portId,
std::vector<AudioRoute*>* routes = nullptr);
const Streams& getStreams() const { return mStreams; }
Type getType() const { return mType; }
bool isMmapSupported();
void populateConnectedProfiles();
template <typename C>
std::set<int32_t> portIdsFromPortConfigIds(C portConfigIds);
void registerPatch(const AudioPatch& patch);
ndk::ScopedAStatus setAudioPortConfigImpl(
const ::aidl::android::media::audio::common::AudioPortConfig& in_requested,
const std::function<bool(const ::aidl::android::media::audio::common::AudioPort& port,
::aidl::android::media::audio::common::AudioPortConfig*
config)>& fillPortConfig,
::aidl::android::media::audio::common::AudioPortConfig* out_suggested, bool* applied);
ndk::ScopedAStatus updateStreamsConnectedState(const AudioPatch& oldPatch,
const AudioPatch& newPatch);
bool setAudioPortConfigGain(
const ::aidl::android::media::audio::common::AudioPort& port,
const ::aidl::android::media::audio::common::AudioGainConfig& gainRequested);
};
std::ostream& operator<<(std::ostream& os, Module::Type t);
} // namespace aidl::android::hardware::audio::core

View File

@@ -0,0 +1,40 @@
/*
* 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 "core-impl/Module.h"
namespace aidl::android::hardware::audio::core {
// This class is intended to be used as a base class for implementations
// that use TinyAlsa. This can be either a primary module or a USB Audio
// module. This class does not define a complete module implementation,
// and should never be used on its own. Derived classes are expected to
// provide necessary overrides for all interface methods omitted here.
class ModuleAlsa : public Module {
public:
ModuleAlsa(Type type, std::unique_ptr<Configuration>&& config)
: Module(type, std::move(config)) {}
protected:
// Extension methods of 'Module'.
ndk::ScopedAStatus populateConnectedDevicePort(
::aidl::android::media::audio::common::AudioPort* audioPort,
int32_t nextPortId) override;
};
} // namespace aidl::android::hardware::audio::core

View File

@@ -0,0 +1,96 @@
/*
* 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 <map>
#include "core-impl/Bluetooth.h"
#include "core-impl/DevicePortProxy.h"
#include "core-impl/Module.h"
namespace aidl::android::hardware::audio::core {
class ModuleBluetooth final : public Module {
public:
enum BtInterface : int { BTSCO, BTA2DP, BTLE };
typedef std::tuple<std::weak_ptr<IBluetooth>, std::weak_ptr<IBluetoothA2dp>,
std::weak_ptr<IBluetoothLe>>
BtProfileHandles;
ModuleBluetooth(std::unique_ptr<Configuration>&& config);
private:
struct CachedProxy {
std::shared_ptr<::android::bluetooth::audio::aidl::BluetoothAudioPortAidl> ptr;
::aidl::android::hardware::bluetooth::audio::PcmConfiguration pcmConfig;
};
ChildInterface<BluetoothA2dp>& getBtA2dp();
ChildInterface<BluetoothLe>& getBtLe();
BtProfileHandles getBtProfileManagerHandles();
ndk::ScopedAStatus getBluetoothA2dp(std::shared_ptr<IBluetoothA2dp>* _aidl_return) override;
ndk::ScopedAStatus getBluetoothLe(std::shared_ptr<IBluetoothLe>* _aidl_return) override;
ndk::ScopedAStatus getMicMute(bool* _aidl_return) override;
ndk::ScopedAStatus setMicMute(bool in_mute) override;
ndk::ScopedAStatus setAudioPortConfig(
const ::aidl::android::media::audio::common::AudioPortConfig& in_requested,
::aidl::android::media::audio::common::AudioPortConfig* out_suggested,
bool* _aidl_return) override;
ndk::ScopedAStatus checkAudioPatchEndpointsMatch(
const std::vector<::aidl::android::media::audio::common::AudioPortConfig*>& sources,
const std::vector<::aidl::android::media::audio::common::AudioPortConfig*>& sinks)
override;
void onExternalDeviceConnectionChanged(
const ::aidl::android::media::audio::common::AudioPort& audioPort, bool connected);
ndk::ScopedAStatus createInputStream(
StreamContext&& context,
const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
std::shared_ptr<StreamIn>* result) override;
ndk::ScopedAStatus createOutputStream(
StreamContext&& context,
const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo,
std::shared_ptr<StreamOut>* result) override;
ndk::ScopedAStatus populateConnectedDevicePort(
::aidl::android::media::audio::common::AudioPort* audioPort,
int32_t nextPortId) override;
ndk::ScopedAStatus onMasterMuteChanged(bool mute) override;
ndk::ScopedAStatus onMasterVolumeChanged(float volume) override;
int32_t getNominalLatencyMs(
const ::aidl::android::media::audio::common::AudioPortConfig& portConfig) override;
ndk::ScopedAStatus createProxy(
const ::aidl::android::media::audio::common::AudioPort& audioPort,
int32_t instancePortId, CachedProxy& proxy);
ndk::ScopedAStatus fetchAndCheckProxy(const StreamContext& context, CachedProxy& proxy);
ndk::ScopedAStatus findOrCreateProxy(
const ::aidl::android::media::audio::common::AudioPort& audioPort, CachedProxy& proxy);
static constexpr int kCreateProxyRetries = 5;
static constexpr int kCreateProxyRetrySleepMs = 75;
ChildInterface<BluetoothA2dp> mBluetoothA2dp;
ChildInterface<BluetoothLe> mBluetoothLe;
std::map<int32_t /*instantiated device port ID*/, CachedProxy> mProxies;
std::map<int32_t /*mix port handle*/, int32_t /*instantiated device port ID*/> mConnections;
};
} // namespace aidl::android::hardware::audio::core

View File

@@ -0,0 +1,55 @@
/*
* 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 "core-impl/Module.h"
namespace aidl::android::hardware::audio::core {
class ModulePrimary final : public Module {
public:
ModulePrimary(std::unique_ptr<Configuration>&& config)
: Module(Type::DEFAULT, std::move(config)) {}
protected:
ndk::ScopedAStatus getTelephony(std::shared_ptr<ITelephony>* _aidl_return) override;
ndk::ScopedAStatus calculateBufferSizeFrames(
const ::aidl::android::media::audio::common::AudioFormatDescription& format,
int32_t latencyMs, int32_t sampleRateHz, int32_t* bufferSizeFrames) override;
ndk::ScopedAStatus createInputStream(
StreamContext&& context,
const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
std::shared_ptr<StreamIn>* result) override;
ndk::ScopedAStatus createOutputStream(
StreamContext&& context,
const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo,
std::shared_ptr<StreamOut>* result) override;
ndk::ScopedAStatus createMmapBuffer(
const ::aidl::android::media::audio::common::AudioPortConfig& portConfig,
int32_t bufferSizeFrames, int32_t frameSizeBytes, MmapBufferDescriptor* desc) override;
int32_t getNominalLatencyMs(
const ::aidl::android::media::audio::common::AudioPortConfig& portConfig) override;
private:
ChildInterface<ITelephony> mTelephony;
};
} // namespace aidl::android::hardware::audio::core

View File

@@ -0,0 +1,63 @@
/*
* 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 "core-impl/Module.h"
namespace aidl::android::hardware::audio::core {
class ModuleRemoteSubmix : public Module {
public:
ModuleRemoteSubmix(std::unique_ptr<Configuration>&& config)
: Module(Type::R_SUBMIX, std::move(config)) {}
private:
// IModule interfaces
ndk::ScopedAStatus getMicMute(bool* _aidl_return) override;
ndk::ScopedAStatus setMicMute(bool in_mute) override;
ndk::ScopedAStatus setAudioPortConfig(
const ::aidl::android::media::audio::common::AudioPortConfig& in_requested,
::aidl::android::media::audio::common::AudioPortConfig* out_suggested,
bool* _aidl_return) override;
// Module interfaces
ndk::ScopedAStatus createInputStream(
StreamContext&& context,
const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
std::shared_ptr<StreamIn>* result) override;
ndk::ScopedAStatus createOutputStream(
StreamContext&& context,
const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo,
std::shared_ptr<StreamOut>* result) override;
ndk::ScopedAStatus populateConnectedDevicePort(
::aidl::android::media::audio::common::AudioPort* audioPort,
int32_t nextPortId) override;
ndk::ScopedAStatus checkAudioPatchEndpointsMatch(
const std::vector<::aidl::android::media::audio::common::AudioPortConfig*>& sources,
const std::vector<::aidl::android::media::audio::common::AudioPortConfig*>& sinks)
override;
ndk::ScopedAStatus onMasterMuteChanged(bool mute) override;
ndk::ScopedAStatus onMasterVolumeChanged(float volume) override;
int32_t getNominalLatencyMs(
const ::aidl::android::media::audio::common::AudioPortConfig& portConfig) override;
binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
};
} // namespace aidl::android::hardware::audio::core

View File

@@ -0,0 +1,50 @@
/*
* 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 "core-impl/Module.h"
namespace aidl::android::hardware::audio::core {
class ModuleStub final : public Module {
public:
ModuleStub(std::unique_ptr<Configuration>&& config) : Module(Type::STUB, std::move(config)) {}
protected:
ndk::ScopedAStatus getBluetooth(std::shared_ptr<IBluetooth>* _aidl_return) override;
ndk::ScopedAStatus getBluetoothA2dp(std::shared_ptr<IBluetoothA2dp>* _aidl_return) override;
ndk::ScopedAStatus getBluetoothLe(std::shared_ptr<IBluetoothLe>* _aidl_return) override;
ndk::ScopedAStatus createInputStream(
StreamContext&& context,
const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
std::shared_ptr<StreamIn>* result) override;
ndk::ScopedAStatus createOutputStream(
StreamContext&& context,
const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo,
std::shared_ptr<StreamOut>* result) override;
private:
ChildInterface<IBluetooth> mBluetooth;
ChildInterface<IBluetoothA2dp> mBluetoothA2dp;
ChildInterface<IBluetoothLe> mBluetoothLe;
};
} // namespace aidl::android::hardware::audio::core

View File

@@ -0,0 +1,60 @@
/*
* 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 "core-impl/ModuleAlsa.h"
namespace aidl::android::hardware::audio::core {
class ModuleUsb final : public ModuleAlsa {
public:
ModuleUsb(std::unique_ptr<Configuration>&& config) : ModuleAlsa(Type::USB, std::move(config)) {}
private:
// IModule interfaces
ndk::ScopedAStatus getTelephony(std::shared_ptr<ITelephony>* _aidl_return) override;
ndk::ScopedAStatus getBluetooth(std::shared_ptr<IBluetooth>* _aidl_return) override;
ndk::ScopedAStatus getMicMute(bool* _aidl_return) override;
ndk::ScopedAStatus setMicMute(bool in_mute) override;
// Module interfaces
ndk::ScopedAStatus createInputStream(
StreamContext&& context,
const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
std::shared_ptr<StreamIn>* result) override;
ndk::ScopedAStatus createOutputStream(
StreamContext&& context,
const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo,
std::shared_ptr<StreamOut>* result) override;
ndk::ScopedAStatus populateConnectedDevicePort(
::aidl::android::media::audio::common::AudioPort* audioPort,
int32_t nextPortId) override;
ndk::ScopedAStatus checkAudioPatchEndpointsMatch(
const std::vector<::aidl::android::media::audio::common::AudioPortConfig*>& sources,
const std::vector<::aidl::android::media::audio::common::AudioPortConfig*>& sinks)
override;
void onExternalDeviceConnectionChanged(
const ::aidl::android::media::audio::common::AudioPort& audioPort,
bool connected) override;
ndk::ScopedAStatus onMasterMuteChanged(bool mute) override;
ndk::ScopedAStatus onMasterVolumeChanged(float volume) override;
};
} // namespace aidl::android::hardware::audio::core

View File

@@ -0,0 +1,87 @@
/*
* 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 <mutex>
#include <aidl/android/hardware/audio/core/sounddose/BnSoundDose.h>
#include <aidl/android/media/audio/common/AudioDevice.h>
#include <aidl/android/media/audio/common/AudioFormatDescription.h>
#include <audio_utils/MelProcessor.h>
#include <audio_utils/mutex.h>
namespace aidl::android::hardware::audio::core::sounddose {
// Interface used for processing the data received by a stream.
class StreamDataProcessorInterface {
public:
virtual ~StreamDataProcessorInterface() = default;
virtual void startDataProcessor(
uint32_t samplerate, uint32_t channelCount,
const ::aidl::android::media::audio::common::AudioFormatDescription& format) = 0;
virtual void setAudioDevice(
const ::aidl::android::media::audio::common::AudioDevice& audioDevice) = 0;
virtual void process(const void* buffer, size_t size) = 0;
};
class SoundDose final : public BnSoundDose, public StreamDataProcessorInterface {
public:
SoundDose() : mMelCallback(::android::sp<MelCallback>::make(this)){};
// -------------------------------------- BnSoundDose ------------------------------------------
ndk::ScopedAStatus setOutputRs2UpperBound(float in_rs2ValueDbA) override;
ndk::ScopedAStatus getOutputRs2UpperBound(float* _aidl_return) override;
ndk::ScopedAStatus registerSoundDoseCallback(
const std::shared_ptr<ISoundDose::IHalSoundDoseCallback>& in_callback) override;
// ----------------------------- StreamDataProcessorInterface ----------------------------------
void setAudioDevice(
const ::aidl::android::media::audio::common::AudioDevice& audioDevice) override;
void startDataProcessor(
uint32_t samplerate, uint32_t channelCount,
const ::aidl::android::media::audio::common::AudioFormatDescription& format) override;
void process(const void* buffer, size_t size) override;
private:
class MelCallback : public ::android::audio_utils::MelProcessor::MelCallback {
public:
explicit MelCallback(SoundDose* soundDose) : mSoundDose(*soundDose) {}
// ------------------------------------ MelCallback ----------------------------------------
void onNewMelValues(const std::vector<float>& mels, size_t offset, size_t length,
audio_port_handle_t deviceId, bool attenuated) const override;
void onMomentaryExposure(float currentMel, audio_port_handle_t deviceId) const override;
SoundDose& mSoundDose; // must outlive MelCallback, not owning
};
void onNewMelValues(const std::vector<float>& mels, size_t offset, size_t length,
audio_port_handle_t deviceId) const;
void onMomentaryExposure(float currentMel, audio_port_handle_t deviceId) const;
mutable ::android::audio_utils::mutex mCbMutex;
std::shared_ptr<ISoundDose::IHalSoundDoseCallback> mCallback GUARDED_BY(mCbMutex);
std::optional<::aidl::android::media::audio::common::AudioDevice> mAudioDevice
GUARDED_BY(mCbMutex);
mutable ::android::audio_utils::mutex mMutex;
float mRs2Value GUARDED_BY(mMutex) = DEFAULT_MAX_RS2;
::android::sp<::android::audio_utils::MelProcessor> mMelProcessor GUARDED_BY(mMutex);
::android::sp<MelCallback> mMelCallback GUARDED_BY(mMutex);
};
} // namespace aidl::android::hardware::audio::core::sounddose

View File

@@ -0,0 +1,740 @@
/*
* 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 <android-base/thread_annotations.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) {}
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, MmapBufferDescriptor&& mmapDesc,
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),
mMmapBufferDesc(std::move(mmapDesc)),
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 isInput() const {
return mFlags.getTag() == ::aidl::android::media::audio::common::AudioIoFlags::input;
}
bool isMmap() const { return ::aidl::android::hardware::audio::common::hasMmapFlag(mFlags); }
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.
int64_t advanceFrameCount(size_t increase) { return mFrameCount += increase; }
int64_t 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;
// Only one of `mDataMQ` or `mMapBufferDesc` can be active, depending on `isMmap`
std::unique_ptr<DataMQ> mDataMQ;
MmapBufferDescriptor mMmapBufferDesc;
std::shared_ptr<IStreamCallback> mAsyncCallback;
std::shared_ptr<IStreamOutEventCallback> mOutEventCallback; // Only used by output streams
std::weak_ptr<sounddose::StreamDataProcessorInterface> mStreamDataProcessor;
DebugParameters mDebugParameters;
int64_t mFrameCount = 0;
};
// Driver callbacks are executed on a dedicated thread, not on the worker thread.
struct DriverCallbackInterface {
virtual ~DriverCallbackInterface() = default;
// Both callbacks are used to notify the worker about the progress of the playback
// offloaded to the DSP.
// 'bufferFramesLeft' is how many *encoded* frames are left in the buffer until
// it depletes.
virtual void onBufferStateChange(size_t bufferFramesLeft) = 0;
// 'clipFramesLeft' is how many *decoded* frames are left until the end of the currently
// playing clip. '0' frames left means that the clip has ended (by itself or due
// to draining).
// 'hasNextClip' indicates whether the DSP has audio data for the next clip.
virtual void onClipStateChange(size_t clipFramesLeft, bool hasNextClip) = 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(DriverCallbackInterface* callback) = 0; // 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 DriverCallbackInterface {
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;
// ::android::hardware::audio::common::StreamLogic
std::string init() override;
// DriverCallbackInterface
void onBufferStateChange(size_t bufferFramesLeft) override;
void onClipStateChange(size_t clipFramesLeft, bool hasNextClip) 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 join() = 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 join() override { return WorkerImpl::join(); }
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);
bool readMmap(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;
// DriverCallbackInterface
void onBufferStateChange(size_t bufferFramesLeft) override;
void onClipStateChange(size_t clipFramesLeft, bool hasNextClip) override;
private:
bool write(size_t clientSize, StreamDescriptor::Reply* reply);
bool writeMmap(StreamDescriptor::Reply* reply);
std::shared_ptr<IStreamOutEventCallback> mEventCallback;
enum DrainState : int32_t { NONE, ALL, EN /*early notify*/, EN_SENT };
std::atomic<DrainState> mDrainState = DrainState::NONE;
};
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;
virtual ndk::ScopedAStatus setGain(float gain) = 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;
ndk::ScopedAStatus setGain(float gain) 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;
// Any stream class implementing 'DriverInterface::shutdown' must call 'cleanupWorker' in
// the destructor in order to stop and join the worker thread in the case when the client
// has not called 'IStreamCommon::close' method.
void cleanupWorker();
void setWorkerThreadPriority(pid_t workerTid);
void stopAndJoinWorker();
void stopWorker();
const StreamContext& mContext;
Metadata mMetadata;
std::unique_ptr<StreamWorkerInterface> mWorker;
ChildInterface<StreamCommonDelegator> mCommon;
ConnectedDevices mConnectedDevices;
private:
std::atomic<bool> mWorkerStopIssued = false;
};
// 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();
}
ndk::ScopedAStatus setGain(float gain) {
auto s = mStream.lock();
if (s) return s->setGain(gain);
return ndk::ScopedAStatus::ok();
}
void dump(int fd, const char** args, uint32_t numArgs) const {
auto s = ::ndk::ICInterface::asInterface(mStreamBinder.get());
if (s) s->dump(fd, args, numArgs);
return;
}
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);
}
ndk::ScopedAStatus setGain(int32_t portId, float gain) {
if (auto it = mStreams.find(portId); it != mStreams.end()) {
return it->second.setGain(gain);
}
return ndk::ScopedAStatus::ok();
}
void dump(int32_t portConfigId, int fd, const char** args, uint32_t numArgs) const {
if (auto it = mStreams.find(portConfigId); it != mStreams.end()) {
it->second.dump(fd, args, numArgs);
}
return;
}
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

View File

@@ -0,0 +1,85 @@
/*
* 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 <atomic>
#include <optional>
#include <thread>
#include <vector>
#include <media/nbaio/MonoPipe.h>
#include <media/nbaio/MonoPipeReader.h>
#include "Stream.h"
#include "alsa/Utils.h"
namespace aidl::android::hardware::audio::core {
// This class is intended to be used as a base class for implementations
// that use TinyAlsa.
// This class does not define a complete stream implementation,
// and should never be used on its own. Derived classes are expected to
// provide necessary overrides for all interface methods omitted here.
class StreamAlsa : public StreamCommonImpl {
public:
StreamAlsa(StreamContext* context, const Metadata& metadata, int readWriteRetries);
~StreamAlsa();
// Methods of 'DriverInterface'.
::android::status_t init(DriverCallbackInterface* callback) override;
::android::status_t drain(StreamDescriptor::DrainMode) override;
::android::status_t flush() override;
::android::status_t pause() override;
::android::status_t standby() override;
::android::status_t start() override;
::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) override;
::android::status_t refinePosition(StreamDescriptor::Position* position) override;
void shutdown() override;
ndk::ScopedAStatus setGain(float gain) override;
protected:
// Called from 'start' to initialize 'mAlsaDeviceProxies', the vector must be non-empty.
virtual std::vector<alsa::DeviceProfile> getDeviceProfiles() = 0;
const size_t mBufferSizeFrames;
const size_t mFrameSizeBytes;
const int mSampleRate;
const bool mIsInput;
const std::optional<struct pcm_config> mConfig;
const int mReadWriteRetries;
private:
::android::NBAIO_Format getPipeFormat() const;
::android::sp<::android::MonoPipe> makeSink(bool writeCanBlock);
::android::sp<::android::MonoPipeReader> makeSource(::android::MonoPipe* pipe);
void inputIoThread(size_t idx);
void outputIoThread(size_t idx);
void teardownIo();
std::atomic<float> mGain = 1.0;
// All fields below are only used on the worker thread.
std::vector<alsa::DeviceProxy> mAlsaDeviceProxies;
// Only 'libnbaio_mono' is vendor-accessible, thus no access to the multi-reader Pipe.
std::vector<::android::sp<::android::MonoPipe>> mSinks;
std::vector<::android::sp<::android::MonoPipeReader>> mSources;
std::vector<std::thread> mIoThreads;
std::atomic<bool> mIoThreadIsRunning = false; // used by all threads
};
} // namespace aidl::android::hardware::audio::core

View File

@@ -0,0 +1,116 @@
/*
* 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 <mutex>
#include <vector>
#include <aidl/android/hardware/audio/core/IBluetooth.h>
#include <aidl/android/hardware/audio/core/IBluetoothA2dp.h>
#include <aidl/android/hardware/audio/core/IBluetoothLe.h>
#include "core-impl/DevicePortProxy.h"
#include "core-impl/ModuleBluetooth.h"
#include "core-impl/Stream.h"
namespace aidl::android::hardware::audio::core {
class StreamBluetooth : public StreamCommonImpl {
public:
static bool checkConfigParams(
const ::aidl::android::hardware::bluetooth::audio::PcmConfiguration& pcmConfig,
const ::aidl::android::media::audio::common::AudioConfigBase& config);
StreamBluetooth(
StreamContext* context, const Metadata& metadata,
ModuleBluetooth::BtProfileHandles&& btHandles,
const std::shared_ptr<::android::bluetooth::audio::aidl::BluetoothAudioPortAidl>&
btDeviceProxy,
const ::aidl::android::hardware::bluetooth::audio::PcmConfiguration& pcmConfig);
~StreamBluetooth();
// Methods of 'DriverInterface'.
::android::status_t init(DriverCallbackInterface*) override;
::android::status_t drain(StreamDescriptor::DrainMode) override;
::android::status_t flush() override;
::android::status_t pause() override;
::android::status_t standby() override;
::android::status_t start() override;
::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) override;
void shutdown() override;
// Overridden methods of 'StreamCommonImpl', called on a Binder thread.
ndk::ScopedAStatus updateMetadataCommon(const Metadata& metadata) override;
ndk::ScopedAStatus prepareToClose() override;
ndk::ScopedAStatus bluetoothParametersUpdated() override;
private:
const size_t mFrameSizeBytes;
const bool mIsInput;
const std::weak_ptr<IBluetoothA2dp> mBluetoothA2dp;
const std::weak_ptr<IBluetoothLe> mBluetoothLe;
const size_t mPreferredDataIntervalUs;
mutable std::mutex mLock;
// The lock is also used to serialize calls to the proxy.
std::shared_ptr<::android::bluetooth::audio::aidl::BluetoothAudioPortAidl> mBtDeviceProxy
GUARDED_BY(mLock); // proxy may be null if the stream is not connected to a device
};
class StreamInBluetooth final : public StreamIn, public StreamBluetooth {
public:
friend class ndk::SharedRefBase;
static int32_t getNominalLatencyMs(size_t dataIntervalUs);
StreamInBluetooth(
StreamContext&& context,
const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
ModuleBluetooth::BtProfileHandles&& btHandles,
const std::shared_ptr<::android::bluetooth::audio::aidl::BluetoothAudioPortAidl>&
btDeviceProxy,
const ::aidl::android::hardware::bluetooth::audio::PcmConfiguration& pcmConfig);
private:
void onClose(StreamDescriptor::State) override { defaultOnClose(); }
ndk::ScopedAStatus getActiveMicrophones(
std::vector<::aidl::android::media::audio::common::MicrophoneDynamicInfo>* _aidl_return)
override;
};
class StreamOutBluetooth final : public StreamOut, public StreamBluetooth {
public:
friend class ndk::SharedRefBase;
static int32_t getNominalLatencyMs(size_t dataIntervalUs);
StreamOutBluetooth(
StreamContext&& context,
const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo,
ModuleBluetooth::BtProfileHandles&& btHandles,
const std::shared_ptr<::android::bluetooth::audio::aidl::BluetoothAudioPortAidl>&
btDeviceProxy,
const ::aidl::android::hardware::bluetooth::audio::PcmConfiguration& pcmConfig);
private:
void onClose(StreamDescriptor::State) override { defaultOnClose(); }
};
} // namespace aidl::android::hardware::audio::core

View File

@@ -0,0 +1,132 @@
/*
* Copyright (C) 2025 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 <mutex>
#include <string>
#include "core-impl/DriverStubImpl.h"
#include "core-impl/Stream.h"
namespace aidl::android::hardware::audio::core {
namespace mmap {
struct DspSimulatorState {
const bool isInput;
const int sampleRate;
const int frameSizeBytes;
const size_t bufferSizeBytes;
std::mutex lock;
// The lock is also used to prevent un-mapping while the memory is in use.
uint8_t* sharedMemory GUARDED_BY(lock) = nullptr;
StreamDescriptor::Position mmapPos GUARDED_BY(lock);
};
class DspSimulatorLogic : public ::android::hardware::audio::common::StreamLogic {
protected:
explicit DspSimulatorLogic(DspSimulatorState& sharedState) : mSharedState(sharedState) {}
std::string init() override;
Status cycle() override;
private:
DspSimulatorState& mSharedState;
uint32_t mCycleDurationUs = 0;
uint8_t* mMemBegin = nullptr;
uint8_t* mMemPos = nullptr;
int64_t mLastFrames = 0;
};
class DspSimulatorWorker
: public ::android::hardware::audio::common::StreamWorker<DspSimulatorLogic> {
public:
explicit DspSimulatorWorker(DspSimulatorState& sharedState)
: ::android::hardware::audio::common::StreamWorker<DspSimulatorLogic>(sharedState) {}
};
} // namespace mmap
class DriverMmapStubImpl : public DriverStubImpl {
public:
explicit DriverMmapStubImpl(const StreamContext& context);
::android::status_t init(DriverCallbackInterface* callback) override;
::android::status_t drain(StreamDescriptor::DrainMode drainMode) override;
::android::status_t pause() override;
::android::status_t start() override;
::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) override;
void shutdown() override;
::android::status_t refinePosition(StreamDescriptor::Position* position) override;
::android::status_t getMmapPositionAndLatency(StreamDescriptor::Position* position,
int32_t* latency) override;
protected:
::android::status_t initSharedMemory(int ashmemFd);
private:
::android::status_t releaseSharedMemory() REQUIRES(mState.lock);
::android::status_t startWorkerIfNeeded();
mmap::DspSimulatorState mState;
mmap::DspSimulatorWorker mDspWorker;
bool mDspWorkerStarted = false;
};
class StreamMmapStub : public StreamCommonImpl, public DriverMmapStubImpl {
public:
static const std::string kCreateMmapBufferName;
StreamMmapStub(StreamContext* context, const Metadata& metadata);
~StreamMmapStub();
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;
private:
ndk::ScopedAStatus createMmapBuffer(MmapBufferDescriptor* desc);
ndk::ScopedFileDescriptor mSharedMemoryFd;
};
class StreamInMmapStub final : public StreamIn, public StreamMmapStub {
public:
friend class ndk::SharedRefBase;
StreamInMmapStub(
StreamContext&& context,
const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones);
private:
void onClose(StreamDescriptor::State) override { defaultOnClose(); }
};
class StreamOutMmapStub final : public StreamOut, public StreamMmapStub {
public:
friend class ndk::SharedRefBase;
StreamOutMmapStub(
StreamContext&& context,
const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo);
private:
void onClose(StreamDescriptor::State) override { defaultOnClose(); }
};
} // namespace aidl::android::hardware::audio::core

View File

@@ -0,0 +1,105 @@
/*
* Copyright (C) 2025 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 <mutex>
#include <set>
#include <string>
#include <vector>
#include "core-impl/DriverStubImpl.h"
#include "core-impl/Stream.h"
namespace aidl::android::hardware::audio::core {
namespace offload {
struct DspSimulatorState {
static constexpr int64_t kSkipBufferNotifyFrames = -1;
const std::string formatEncoding;
const int sampleRate;
const int64_t earlyNotifyFrames;
DriverCallbackInterface* callback = nullptr; // set before starting DSP worker
std::mutex lock;
std::vector<int64_t> clipFramesLeft GUARDED_BY(lock);
int64_t bufferFramesLeft GUARDED_BY(lock) = 0;
int64_t bufferNotifyFrames GUARDED_BY(lock) = kSkipBufferNotifyFrames;
};
class DspSimulatorLogic : public ::android::hardware::audio::common::StreamLogic {
protected:
explicit DspSimulatorLogic(DspSimulatorState& sharedState) : mSharedState(sharedState) {}
std::string init() override;
Status cycle() override;
private:
DspSimulatorState& mSharedState;
};
class DspSimulatorWorker
: public ::android::hardware::audio::common::StreamWorker<DspSimulatorLogic> {
public:
explicit DspSimulatorWorker(DspSimulatorState& sharedState)
: ::android::hardware::audio::common::StreamWorker<DspSimulatorLogic>(sharedState) {}
};
} // namespace offload
class DriverOffloadStubImpl : public DriverStubImpl {
public:
explicit DriverOffloadStubImpl(const StreamContext& context);
::android::status_t init(DriverCallbackInterface* callback) override;
::android::status_t drain(StreamDescriptor::DrainMode drainMode) override;
::android::status_t flush() override;
::android::status_t pause() override;
::android::status_t start() override;
::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) override;
void shutdown() override;
private:
::android::status_t startWorkerIfNeeded();
const int64_t mBufferNotifyFrames;
offload::DspSimulatorState mState;
offload::DspSimulatorWorker mDspWorker;
bool mDspWorkerStarted = false;
};
class StreamOffloadStub : public StreamCommonImpl, public DriverOffloadStubImpl {
public:
static const std::set<std::string>& getSupportedEncodings();
StreamOffloadStub(StreamContext* context, const Metadata& metadata);
~StreamOffloadStub();
};
class StreamOutOffloadStub final : public StreamOut, public StreamOffloadStub {
public:
friend class ndk::SharedRefBase;
StreamOutOffloadStub(
StreamContext&& context,
const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo);
private:
void onClose(StreamDescriptor::State) override { defaultOnClose(); }
};
} // namespace aidl::android::hardware::audio::core

View File

@@ -0,0 +1,113 @@
/*
* 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 <mutex>
#include <vector>
#include <android-base/thread_annotations.h>
#include "DriverStubImpl.h"
#include "StreamAlsa.h"
#include "primary/PrimaryMixer.h"
namespace aidl::android::hardware::audio::core {
class StreamPrimary : public StreamAlsa {
public:
StreamPrimary(StreamContext* context, const Metadata& metadata);
// Methods of 'DriverInterface'.
::android::status_t init(DriverCallbackInterface* callback) override;
::android::status_t drain(StreamDescriptor::DrainMode mode) override;
::android::status_t flush() override;
::android::status_t pause() override;
::android::status_t standby() override;
::android::status_t start() override;
::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) override;
::android::status_t refinePosition(StreamDescriptor::Position* position) override;
void shutdown() override;
// Overridden methods of 'StreamCommonImpl', called on a Binder thread.
ndk::ScopedAStatus setConnectedDevices(const ConnectedDevices& devices) override;
protected:
std::vector<alsa::DeviceProfile> getDeviceProfiles() override;
bool isStubStream();
const bool mIsAsynchronous;
int64_t mStartTimeNs = 0;
long mFramesSinceStart = 0;
bool mSkipNextTransfer = false;
private:
using AlsaDeviceId = std::pair<int, int>;
static constexpr StreamPrimary::AlsaDeviceId kDefaultCardAndDeviceId{
primary::PrimaryMixer::kAlsaCard, primary::PrimaryMixer::kAlsaDevice};
static constexpr StreamPrimary::AlsaDeviceId kStubDeviceId{
primary::PrimaryMixer::kInvalidAlsaCard, primary::PrimaryMixer::kInvalidAlsaDevice};
static AlsaDeviceId getCardAndDeviceId(
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices);
static bool useStubStream(bool isInput,
const ::aidl::android::media::audio::common::AudioDevice& device);
bool isStubStreamOnWorker() const { return mCurrAlsaDeviceId == kStubDeviceId; }
DriverStubImpl mStubDriver;
mutable std::mutex mLock;
AlsaDeviceId mAlsaDeviceId GUARDED_BY(mLock) = kStubDeviceId;
// Used by the worker thread only.
AlsaDeviceId mCurrAlsaDeviceId = kStubDeviceId;
};
class StreamInPrimary final : public StreamIn, public StreamPrimary, public StreamInHwGainHelper {
public:
friend class ndk::SharedRefBase;
StreamInPrimary(
StreamContext&& context,
const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones);
private:
void onClose(StreamDescriptor::State) override { defaultOnClose(); }
ndk::ScopedAStatus getHwGain(std::vector<float>* _aidl_return) override;
ndk::ScopedAStatus setHwGain(const std::vector<float>& in_channelGains) override;
};
class StreamOutPrimary final : public StreamOut,
public StreamPrimary,
public StreamOutHwVolumeHelper {
public:
friend class ndk::SharedRefBase;
StreamOutPrimary(StreamContext&& context,
const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo);
private:
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;
};
} // namespace aidl::android::hardware::audio::core

View File

@@ -0,0 +1,116 @@
/*
* 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 <vector>
#include "core-impl/Stream.h"
#include "deprecated/StreamSwitcher.h"
#include "r_submix/SubmixRoute.h"
namespace aidl::android::hardware::audio::core {
class StreamRemoteSubmix : public StreamCommonImpl {
public:
StreamRemoteSubmix(
StreamContext* context, const Metadata& metadata,
const ::aidl::android::media::audio::common::AudioDeviceAddress& deviceAddress);
~StreamRemoteSubmix();
// Methods of 'DriverInterface'.
::android::status_t init(DriverCallbackInterface*) override;
::android::status_t drain(StreamDescriptor::DrainMode) override;
::android::status_t flush() override;
::android::status_t pause() override;
::android::status_t standby() override;
::android::status_t start() override;
::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) override;
::android::status_t refinePosition(StreamDescriptor::Position* position) override;
void shutdown() override;
// Overridden methods of 'StreamCommonImpl', called on a Binder thread.
ndk::ScopedAStatus prepareToClose() override;
private:
long getDelayInUsForFrameCount(size_t frameCount);
size_t getStreamPipeSizeInFrames();
::android::status_t outWrite(void* buffer, size_t frameCount, size_t* actualFrameCount);
::android::status_t inRead(void* buffer, size_t frameCount, size_t* actualFrameCount);
const ::aidl::android::media::audio::common::AudioDeviceAddress mDeviceAddress;
const bool mIsInput;
r_submix::AudioConfig mStreamConfig;
std::shared_ptr<r_submix::SubmixRoute> mCurrentRoute = nullptr;
// Limit for the number of error log entries to avoid spamming the logs.
static constexpr int kMaxErrorLogs = 5;
// The duration of kMaxReadFailureAttempts * READ_ATTEMPT_SLEEP_MS must be strictly inferior
// to the duration of a record buffer at the current record sample rate (of the device, not of
// the recording itself). Here we have: 3 * 5ms = 15ms < 1024 frames * 1000 / 48000 = 21.333ms
static constexpr int kMaxReadFailureAttempts = 3;
// 5ms between two read attempts when pipe is empty
static constexpr int kReadAttemptSleepUs = 5000;
int64_t mStartTimeNs = 0;
long mFramesSinceStart = 0;
int mReadErrorCount = 0;
int mReadFailureCount = 0;
int mWriteShutdownCount = 0;
};
class StreamInRemoteSubmix final : public StreamIn, public deprecated::StreamSwitcher {
public:
friend class ndk::SharedRefBase;
StreamInRemoteSubmix(
StreamContext&& context,
const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones);
private:
DeviceSwitchBehavior switchCurrentStream(
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices)
override;
std::unique_ptr<deprecated::StreamCommonInterfaceEx> createNewStream(
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices,
StreamContext* context, const Metadata& metadata) override;
void onClose(StreamDescriptor::State) override { defaultOnClose(); }
ndk::ScopedAStatus getActiveMicrophones(
std::vector<::aidl::android::media::audio::common::MicrophoneDynamicInfo>* _aidl_return)
override;
};
class StreamOutRemoteSubmix final : public StreamOut, public deprecated::StreamSwitcher {
public:
friend class ndk::SharedRefBase;
StreamOutRemoteSubmix(
StreamContext&& context,
const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo);
private:
DeviceSwitchBehavior switchCurrentStream(
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices)
override;
std::unique_ptr<deprecated::StreamCommonInterfaceEx> createNewStream(
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices,
StreamContext* context, const Metadata& metadata) override;
void onClose(StreamDescriptor::State) override { defaultOnClose(); }
};
} // namespace aidl::android::hardware::audio::core

View File

@@ -0,0 +1,54 @@
/*
* 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 "core-impl/DriverStubImpl.h"
#include "core-impl/Stream.h"
namespace aidl::android::hardware::audio::core {
class StreamStub : public StreamCommonImpl, public DriverStubImpl {
public:
StreamStub(StreamContext* context, const Metadata& metadata);
~StreamStub();
};
class StreamInStub final : public StreamIn, public StreamStub {
public:
friend class ndk::SharedRefBase;
StreamInStub(
StreamContext&& context,
const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones);
private:
void onClose(StreamDescriptor::State) override { defaultOnClose(); }
};
class StreamOutStub final : public StreamOut, public StreamStub {
public:
friend class ndk::SharedRefBase;
StreamOutStub(StreamContext&& context,
const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo);
private:
void onClose(StreamDescriptor::State) override { defaultOnClose(); }
};
} // namespace aidl::android::hardware::audio::core

View File

@@ -0,0 +1,77 @@
/*
* 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 <atomic>
#include <mutex>
#include <vector>
#include <aidl/android/media/audio/common/AudioChannelLayout.h>
#include "StreamAlsa.h"
namespace aidl::android::hardware::audio::core {
class StreamUsb : public StreamAlsa {
public:
StreamUsb(StreamContext* context, const Metadata& metadata);
// Methods of 'DriverInterface'.
::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) override;
// Overridden methods of 'StreamCommonImpl', called on a Binder thread.
ndk::ScopedAStatus setConnectedDevices(const ConnectedDevices& devices) override;
protected:
std::vector<alsa::DeviceProfile> getDeviceProfiles() override;
mutable std::mutex mLock;
std::vector<alsa::DeviceProfile> mConnectedDeviceProfiles GUARDED_BY(mLock);
std::atomic<bool> mConnectedDevicesUpdated = false;
};
class StreamInUsb final : public StreamIn, public StreamUsb {
public:
friend class ndk::SharedRefBase;
StreamInUsb(
StreamContext&& context,
const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones);
private:
void onClose(StreamDescriptor::State) override { defaultOnClose(); }
ndk::ScopedAStatus getActiveMicrophones(
std::vector<::aidl::android::media::audio::common::MicrophoneDynamicInfo>* _aidl_return)
override;
};
class StreamOutUsb final : public StreamOut, public StreamUsb, public StreamOutHwVolumeHelper {
public:
friend class ndk::SharedRefBase;
StreamOutUsb(StreamContext&& context,
const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo);
private:
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;
};
} // namespace aidl::android::hardware::audio::core

View File

@@ -0,0 +1,47 @@
/*
* 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 <android/binder_enums.h>
#include <aidl/android/hardware/audio/core/BnTelephony.h>
namespace aidl::android::hardware::audio::core {
class Telephony : public BnTelephony {
public:
Telephony();
private:
ndk::ScopedAStatus getSupportedAudioModes(
std::vector<::aidl::android::media::audio::common::AudioMode>* _aidl_return) override;
ndk::ScopedAStatus switchAudioMode(
::aidl::android::media::audio::common::AudioMode in_mode) override;
ndk::ScopedAStatus setTelecomConfig(const TelecomConfig& in_config,
TelecomConfig* _aidl_return) override;
const std::vector<::aidl::android::media::audio::common::AudioMode> mSupportedAudioModes = {
::aidl::android::media::audio::common::AudioMode::NORMAL,
::aidl::android::media::audio::common::AudioMode::RINGTONE,
::aidl::android::media::audio::common::AudioMode::IN_CALL,
::aidl::android::media::audio::common::AudioMode::IN_COMMUNICATION,
// Omit CALL_SCREEN for a better VTS coverage.
};
TelecomConfig mTelecomConfig;
};
} // namespace aidl::android::hardware::audio::core

View File

@@ -0,0 +1,162 @@
/*
* 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 <optional>
#include <string>
#include <unordered_map>
#include <media/AidlConversionUtil.h>
#include <system/audio_config.h>
namespace aidl::android::hardware::audio::core::internal {
template <typename T>
class XmlConverter {
public:
XmlConverter(const std::string& configFilePath,
std::function<std::optional<T>(const char*)> readXmlConfig)
: XmlConverter(configFilePath,
::android::audio_is_readable_configuration_file(configFilePath.c_str()),
readXmlConfig) {}
const ::android::status_t& getStatus() const { return mStatus; }
const std::string& getError() const { return mErrorMessage; }
const std::optional<T>& getXsdcConfig() const { return mXsdcConfig; }
private:
XmlConverter(const std::string& configFilePath, const bool& isReadableConfigFile,
const std::function<std::optional<T>(const char*)>& readXmlConfig)
: mXsdcConfig{isReadableConfigFile ? readXmlConfig(configFilePath.c_str()) : std::nullopt},
mStatus(mXsdcConfig ? ::android::OK : ::android::NO_INIT),
mErrorMessage(generateError(configFilePath, isReadableConfigFile, mStatus)) {}
static std::string generateError(const std::string& configFilePath,
const bool& isReadableConfigFile,
const ::android::status_t& status) {
std::string errorMessage;
if (status != ::android::OK) {
if (configFilePath.empty()) {
errorMessage = "No audio configuration files found";
} else if (!isReadableConfigFile) {
errorMessage = std::string("Could not read requested XML config file: \"")
.append(configFilePath)
.append("\"");
} else {
errorMessage = std::string("Invalid XML config file: \"")
.append(configFilePath)
.append("\"");
}
}
return errorMessage;
}
const std::optional<T> mXsdcConfig;
const ::android::status_t mStatus;
const std::string mErrorMessage;
};
/**
* Converts a vector of an xsd wrapper type to a flat vector of the
* corresponding AIDL type.
*
* Wrapper types are used in order to have well-formed xIncludes. In the
* example below, Modules is the wrapper type for Module.
* <Modules>
* <Module> ... </Module>
* <Module> ... </Module>
* </Modules>
*/
template <typename W, typename X, typename A>
static ConversionResult<std::vector<A>> convertWrappedCollectionToAidl(
const std::vector<W>& xsdcWrapperTypeVec,
std::function<const std::vector<X>&(const W&)> getInnerTypeVec,
std::function<ConversionResult<A>(const X&)> convertToAidl) {
std::vector<A> resultAidlTypeVec;
if (!xsdcWrapperTypeVec.empty()) {
/*
* xsdcWrapperTypeVec likely only contains one element; that is, it's
* likely that all the inner types that we need to convert are inside of
* xsdcWrapperTypeVec[0].
*/
resultAidlTypeVec.reserve(getInnerTypeVec(xsdcWrapperTypeVec[0]).size());
for (const W& xsdcWrapperType : xsdcWrapperTypeVec) {
for (const X& xsdcType : getInnerTypeVec(xsdcWrapperType)) {
resultAidlTypeVec.push_back(VALUE_OR_FATAL(convertToAidl(xsdcType)));
}
}
}
return resultAidlTypeVec;
}
template <typename X, typename A>
static ConversionResult<std::vector<std::optional<A>>> convertCollectionToAidlOptionalValues(
const std::vector<X>& xsdcTypeVec,
std::function<ConversionResult<A>(const X&)> convertToAidl) {
std::vector<std::optional<A>> resultAidlTypeVec;
resultAidlTypeVec.reserve(xsdcTypeVec.size());
for (const X& xsdcType : xsdcTypeVec) {
resultAidlTypeVec.push_back(
std::optional<A>(std::move(VALUE_OR_FATAL(convertToAidl(xsdcType)))));
}
return resultAidlTypeVec;
}
template <typename X, typename A>
static ConversionResult<std::vector<A>> convertCollectionToAidl(
const std::vector<X>& xsdcTypeVec,
std::function<ConversionResult<A>(const X&)> convertToAidl) {
std::vector<A> resultAidlTypeVec;
resultAidlTypeVec.reserve(xsdcTypeVec.size());
for (const X& xsdcType : xsdcTypeVec) {
resultAidlTypeVec.push_back(VALUE_OR_FATAL(convertToAidl(xsdcType)));
}
return resultAidlTypeVec;
}
/**
* Generates a map of xsd references, keyed by reference name, given a
* vector of wrapper types for the reference.
*
* Wrapper types are used in order to have well-formed xIncludes. In the
* example below, Wrapper is the wrapper type for Reference.
* <Wrapper>
* <Reference> ... </Reference>
* <Reference> ... </Reference>
* </Wrapper>
*/
template <typename W, typename R>
std::unordered_map<std::string, R> generateReferenceMap(const std::vector<W>& xsdcWrapperTypeVec) {
std::unordered_map<std::string, R> resultMap;
if (!xsdcWrapperTypeVec.empty()) {
/*
* xsdcWrapperTypeVec likely only contains one element; that is, it's
* likely that all the inner types that we need to convert are inside of
* xsdcWrapperTypeVec[0].
*/
resultMap.reserve(xsdcWrapperTypeVec[0].getReference().size());
for (const W& xsdcWrapperType : xsdcWrapperTypeVec) {
for (const R& xsdcReference : xsdcWrapperType.getReference()) {
resultMap.insert({xsdcReference.getName(), xsdcReference});
}
}
}
return resultMap;
}
} // namespace aidl::android::hardware::audio::core::internal

View File

@@ -0,0 +1,68 @@
/*
* Copyright (C) 2024 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 <string>
#include <aidl/android/media/audio/common/AudioHalCapCriterion.h>
#include <aidl/android/media/audio/common/AudioHalCapCriterionType.h>
#include <aidl/android/media/audio/common/AudioHalCapCriterionV2.h>
#include <aidl/android/media/audio/common/AudioHalVolumeCurve.h>
#include <aidl/android/media/audio/common/AudioPort.h>
#include <android_audio_policy_configuration.h>
#include <android_audio_policy_configuration_enums.h>
#include <android_audio_policy_engine_configuration.h>
#include <media/AidlConversionUtil.h>
#include "core-impl/Module.h"
namespace aidl::android::hardware::audio::core::internal {
namespace engineconfiguration = ::android::audio::policy::engine::configuration;
namespace aidlaudiocommon = ::aidl::android::media::audio::common;
static constexpr const char kXsdcForceConfigForUse[] = "ForceUseFor";
ConversionResult<aidlaudiocommon::AudioPolicyForceUse> convertForceUseToAidl(
const std::string& xsdcCriterionName, const std::string& xsdcCriterionValue);
ConversionResult<aidlaudiocommon::AudioDeviceAddress> convertDeviceAddressToAidl(
const std::string& xsdcAddress);
ConversionResult<aidlaudiocommon::AudioMode> convertTelephonyModeToAidl(
const std::string& xsdcModeCriterionType);
ConversionResult<aidlaudiocommon::AudioDeviceDescription> convertDeviceTypeToAidl(
const std::string& xType);
ConversionResult<std::vector<std::optional<aidlaudiocommon::AudioHalCapCriterionV2>>>
convertCapCriteriaCollectionToAidl(
const std::vector<engineconfiguration::CriteriaType>& xsdcCriteriaVec,
const std::vector<engineconfiguration::CriterionTypesType>& xsdcCriterionTypesVec);
ConversionResult<aidlaudiocommon::AudioHalCapCriterionV2> convertCapCriterionV2ToAidl(
const engineconfiguration::CriterionType& xsdcCriterion,
const std::vector<engineconfiguration::CriterionTypesType>& xsdcCriterionTypesVec);
ConversionResult<aidlaudiocommon::AudioHalVolumeCurve::CurvePoint> convertCurvePointToAidl(
const std::string& xsdcCurvePoint);
ConversionResult<std::unique_ptr<Module::Configuration>> convertModuleConfigToAidl(
const ::android::audio::policy::configuration::Modules::Module& moduleConfig);
ConversionResult<aidlaudiocommon::AudioUsage> convertAudioUsageToAidl(
const engineconfiguration::UsageEnumType& xsdcUsage);
ConversionResult<aidlaudiocommon::AudioContentType> convertAudioContentTypeToAidl(
const engineconfiguration::ContentType& xsdcContentType);
ConversionResult<aidlaudiocommon::AudioSource> convertAudioSourceToAidl(
const engineconfiguration::SourceEnumType& xsdcSourceType);
ConversionResult<aidlaudiocommon::AudioStreamType> convertAudioStreamTypeToAidl(
const engineconfiguration::Stream& xsdStreamType);
ConversionResult<int32_t> convertAudioFlagsToAidl(
const std::vector<engineconfiguration::FlagType>& xsdcFlagTypeVec);
std::unordered_map<std::string, int> getLegacyProductStrategyMap();
} // namespace aidl::android::hardware::audio::core::internal

View File

@@ -0,0 +1,122 @@
/*
* 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 <algorithm>
#include <map>
#include <set>
#include <vector>
namespace aidl::android::hardware::audio::core {
// Return whether all the elements in the vector are unique.
template <typename T>
bool all_unique(const std::vector<T>& v) {
return std::set<T>(v.begin(), v.end()).size() == v.size();
}
// Erase all the specified elements from a map.
template <typename C, typename V>
auto erase_all(C& c, const V& keys) {
auto oldSize = c.size();
for (auto& k : keys) {
c.erase(k);
}
return oldSize - c.size();
}
// Erase all the elements in the container that satisfy the provided predicate.
template <typename C, typename P>
auto erase_if(C& c, P pred) {
auto oldSize = c.size();
for (auto it = c.begin(); it != c.end();) {
if (pred(*it)) {
it = c.erase(it);
} else {
++it;
}
}
return oldSize - c.size();
}
// Erase all the elements in the map that have specified values.
template <typename C, typename V>
auto erase_all_values(C& c, const V& values) {
return erase_if(c, [values](const auto& pair) { return values.count(pair.second) != 0; });
}
// Return non-zero count of elements for any of the provided keys.
template <typename M, typename V>
size_t count_any(const M& m, const V& keys) {
for (auto& k : keys) {
if (size_t c = m.count(k); c != 0) return c;
}
return 0;
}
// Assuming that M is a map whose values have an 'id' field,
// find an element with the specified id.
template <typename M>
auto findById(M& m, int32_t id) {
return std::find_if(m.begin(), m.end(), [&](const auto& p) { return p.second.id == id; });
}
// Assuming that the vector contains elements with an 'id' field,
// find an element with the specified id.
template <typename T>
auto findById(std::vector<T>& v, int32_t id) {
return std::find_if(v.begin(), v.end(), [&](const auto& e) { return e.id == id; });
}
// Return elements from the vector that have specified ids, also
// optionally return which ids were not found.
template <typename T>
std::vector<T*> selectByIds(std::vector<T>& v, const std::vector<int32_t>& ids,
std::vector<int32_t>* missingIds = nullptr) {
std::vector<T*> result;
std::set<int32_t> idsSet(ids.begin(), ids.end());
for (size_t i = 0; i < v.size(); ++i) {
T& e = v[i];
if (idsSet.count(e.id) != 0) {
result.push_back(&v[i]);
idsSet.erase(e.id);
}
}
if (missingIds) {
*missingIds = std::vector(idsSet.begin(), idsSet.end());
}
return result;
}
// Assuming that M is a map whose keys' type is K and values' type is V,
// return the corresponding value of the given key from the map or default
// value if the key is not found.
template <typename M, typename K, typename V>
auto findValueOrDefault(const M& m, const K& key, V defaultValue) {
auto it = m.find(key);
return it == m.end() ? defaultValue : it->second;
}
// Assuming that M is a map whose keys' type is K, return the given key if it
// is found from the map or default value.
template <typename M, typename K>
auto findKeyOrDefault(const M& m, const K& key, K defaultValue) {
auto it = m.find(key);
return it == m.end() ? defaultValue : key;
}
} // namespace aidl::android::hardware::audio::core

View File

@@ -0,0 +1,121 @@
/*
* 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 <memory>
#include <vector>
#include <Utils.h>
#include <android-base/logging.h>
#include <fmq/AidlMessageQueue.h>
#include <fmq/EventFlag.h>
#include <aidl/android/hardware/audio/effect/BnEffect.h>
#include "EffectTypes.h"
namespace aidl::android::hardware::audio::effect {
class EffectContext {
public:
typedef ::android::AidlMessageQueue<
IEffect::Status, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
StatusMQ;
typedef ::android::AidlMessageQueue<
float, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
DataMQ;
EffectContext(size_t statusDepth, const Parameter::Common& common);
virtual ~EffectContext() {
if (mEfGroup) {
::android::hardware::EventFlag::deleteEventFlag(&mEfGroup);
}
}
void setVersion(int version) { mVersion = version; }
std::shared_ptr<StatusMQ> getStatusFmq() const;
std::shared_ptr<DataMQ> getInputDataFmq() const;
std::shared_ptr<DataMQ> getOutputDataFmq() const;
float* getWorkBuffer();
size_t getWorkBufferSize() const;
// reset buffer status by abandon input data in FMQ
void resetBuffer();
void dupeFmq(IEffect::OpenEffectReturn* effectRet);
size_t getInputFrameSize() const;
size_t getOutputFrameSize() const;
int getSessionId() const;
int getIoHandle() const;
virtual void dupeFmqWithReopen(IEffect::OpenEffectReturn* effectRet);
virtual RetCode setOutputDevice(
const std::vector<aidl::android::media::audio::common::AudioDeviceDescription>& device);
virtual std::vector<aidl::android::media::audio::common::AudioDeviceDescription>
getOutputDevice();
virtual RetCode setAudioMode(const aidl::android::media::audio::common::AudioMode& mode);
virtual aidl::android::media::audio::common::AudioMode getAudioMode();
virtual RetCode setAudioSource(const aidl::android::media::audio::common::AudioSource& source);
virtual aidl::android::media::audio::common::AudioSource getAudioSource();
virtual RetCode setVolumeStereo(const Parameter::VolumeStereo& volumeStereo);
virtual Parameter::VolumeStereo getVolumeStereo();
virtual RetCode setCommon(const Parameter::Common& common);
virtual Parameter::Common getCommon();
virtual ::android::hardware::EventFlag* getStatusEventFlag();
virtual RetCode enable();
virtual RetCode disable();
virtual RetCode reset();
virtual RetCode startDraining();
virtual RetCode finishDraining();
virtual bool isDraining();
protected:
bool mIsDraining = false;
int mVersion = 0;
size_t mInputFrameSize = 0;
size_t mOutputFrameSize = 0;
size_t mInputChannelCount = 0;
size_t mOutputChannelCount = 0;
Parameter::Common mCommon = {};
std::vector<aidl::android::media::audio::common::AudioDeviceDescription> mOutputDevice = {};
aidl::android::media::audio::common::AudioMode mMode =
aidl::android::media::audio::common::AudioMode::SYS_RESERVED_INVALID;
aidl::android::media::audio::common::AudioSource mSource =
aidl::android::media::audio::common::AudioSource::SYS_RESERVED_INVALID;
Parameter::VolumeStereo mVolumeStereo = {};
RetCode updateIOFrameSize(const Parameter::Common& common);
RetCode notifyDataMqUpdate();
private:
// fmq and buffers
std::shared_ptr<StatusMQ> mStatusMQ = nullptr;
std::shared_ptr<DataMQ> mInputMQ = nullptr;
std::shared_ptr<DataMQ> mOutputMQ = nullptr;
// std::shared_ptr<IEffect::OpenEffectReturn> mRet;
// work buffer set by effect instances, the access and update are in same thread
std::vector<float> mWorkBuffer = {};
::android::hardware::EventFlag* mEfGroup = nullptr;
};
} // namespace aidl::android::hardware::audio::effect

View File

@@ -0,0 +1,122 @@
/*
* 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 <cstdlib>
#include <memory>
#include <aidl/android/hardware/audio/effect/BnEffect.h>
#include <fmq/AidlMessageQueue.h>
#include "EffectContext.h"
#include "EffectThread.h"
#include "EffectTypes.h"
#include "effect-impl/EffectContext.h"
#include "effect-impl/EffectThread.h"
#include "effect-impl/EffectTypes.h"
extern "C" binder_exception_t destroyEffect(
const std::shared_ptr<aidl::android::hardware::audio::effect::IEffect>& instanceSp);
namespace aidl::android::hardware::audio::effect {
class EffectImpl : public BnEffect, public EffectThread {
public:
EffectImpl() = default;
virtual ~EffectImpl() = default;
virtual ndk::ScopedAStatus open(const Parameter::Common& common,
const std::optional<Parameter::Specific>& specific,
OpenEffectReturn* ret) override;
virtual ndk::ScopedAStatus close() override;
virtual ndk::ScopedAStatus command(CommandId id) override;
virtual ndk::ScopedAStatus reopen(OpenEffectReturn* ret) override;
virtual ndk::ScopedAStatus getState(State* state) override;
virtual ndk::ScopedAStatus setParameter(const Parameter& param) override;
virtual ndk::ScopedAStatus getParameter(const Parameter::Id& id, Parameter* param) override;
virtual ndk::ScopedAStatus setParameterCommon(const Parameter& param) REQUIRES(mImplMutex);
virtual ndk::ScopedAStatus getParameterCommon(const Parameter::Tag& tag, Parameter* param)
REQUIRES(mImplMutex);
/* Methods MUST be implemented by each effect instances */
virtual ndk::ScopedAStatus getDescriptor(Descriptor* desc) = 0;
virtual ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific)
REQUIRES(mImplMutex) = 0;
virtual ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id,
Parameter::Specific* specific)
REQUIRES(mImplMutex) = 0;
virtual std::string getEffectName() = 0;
virtual std::shared_ptr<EffectContext> createContext(const Parameter::Common& common)
REQUIRES(mImplMutex);
virtual RetCode releaseContext() REQUIRES(mImplMutex) = 0;
/**
* @brief effectProcessImpl is running in worker thread which created in EffectThread.
*
* EffectThread will make sure effectProcessImpl only be called after startThread() successful
* and before stopThread() successful.
*
* effectProcessImpl implementation must not call any EffectThread interface, otherwise it will
* cause deadlock.
*
* @param in address of input float buffer.
* @param out address of output float buffer.
* @param samples number of samples to process.
* @return IEffect::Status
*/
virtual IEffect::Status effectProcessImpl(float* in, float* out, int samples) = 0;
/**
* process() get data from data MQs, and call effectProcessImpl() for effect data processing.
* Its important for the implementation to use mImplMutex for context synchronization.
*/
void process() override;
protected:
// current Hal version
int mVersion = 0;
// Use kEventFlagNotEmpty for V1 HAL, kEventFlagDataMqNotEmpty for V2 and above
int mDataMqNotEmptyEf = aidl::android::hardware::audio::effect::kEventFlagDataMqNotEmpty;
State mState GUARDED_BY(mImplMutex) = State::INIT;
IEffect::Status status(binder_status_t status, size_t consumed, size_t produced);
void cleanUp();
std::mutex mImplMutex;
std::shared_ptr<EffectContext> mImplContext GUARDED_BY(mImplMutex);
/**
* Optional CommandId handling methods for effects to override.
* For CommandId::START, EffectImpl call commandImpl before starting the EffectThread
* processing.
* For CommandId::STOP and CommandId::RESET, EffectImpl call commandImpl after stop the
* EffectThread processing.
*/
virtual ndk::ScopedAStatus commandImpl(CommandId id) REQUIRES(mImplMutex);
RetCode notifyEventFlag(uint32_t flag);
std::string getEffectNameWithVersion() {
return getEffectName() + "V" + std::to_string(mVersion);
}
::android::hardware::EventFlag* mEventFlag;
};
} // namespace aidl::android::hardware::audio::effect

View File

@@ -0,0 +1,48 @@
/*
* 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 <algorithm>
#include <tuple>
#include <utility>
#include <vector>
namespace aidl::android::hardware::audio::effect {
template <typename T>
bool isInRange(const T& value, const T& low, const T& high) {
return (value >= low) && (value <= high);
}
template <typename T, std::size_t... Is>
bool isTupleInRange(const T& test, const T& min, const T& max, std::index_sequence<Is...>) {
return (isInRange(std::get<Is>(test), std::get<Is>(min), std::get<Is>(max)) && ...);
}
template <typename T, std::size_t TupSize = std::tuple_size_v<T>>
bool isTupleInRange(const T& test, const T& min, const T& max) {
return isTupleInRange(test, min, max, std::make_index_sequence<TupSize>{});
}
template <typename T, typename F>
bool isTupleInRange(const std::vector<T>& cfgs, const T& min, const T& max, const F& func) {
auto minT = func(min), maxT = func(max);
return std::all_of(cfgs.cbegin(), cfgs.cend(),
[&](const T& cfg) { return isTupleInRange(func(cfg), minT, maxT); });
}
} // namespace aidl::android::hardware::audio::effect

View File

@@ -0,0 +1,69 @@
/*
* 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 <memory>
#include <string>
#include <thread>
#include <android-base/thread_annotations.h>
#include <fmq/EventFlag.h>
#include <system/thread_defs.h>
#include "effect-impl/EffectContext.h"
#include "effect-impl/EffectTypes.h"
namespace aidl::android::hardware::audio::effect {
class EffectThread {
public:
virtual ~EffectThread();
// called by effect implementation
RetCode createThread(const std::string& name, int priority = ANDROID_PRIORITY_URGENT_AUDIO);
RetCode destroyThread();
RetCode startThread();
RetCode stopThread();
RetCode startDraining();
RetCode finishDraining();
// Will call process() in a loop if the thread is running.
void threadLoop();
/**
* process() call effectProcessImpl() for effect data processing, it is necessary for the
* processing to be called under Effect thread mutex mThreadMutex, to avoid the effect state
* change before/during data processing, and keep the thread and effect state consistent.
*/
virtual void process() = 0;
protected:
bool mDraining GUARDED_BY(mThreadMutex) = false;
private:
static constexpr int kMaxTaskNameLen = 15;
std::mutex mThreadMutex;
std::condition_variable mCv;
bool mStop GUARDED_BY(mThreadMutex) = true;
bool mExit GUARDED_BY(mThreadMutex) = false;
std::thread mThread;
int mPriority;
std::string mName;
};
} // namespace aidl::android::hardware::audio::effect

View File

@@ -0,0 +1,133 @@
/*
* 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 <string>
#include <aidl/android/hardware/audio/effect/BnEffect.h>
#include <aidl/android/hardware/audio/effect/Range.h>
#include <android-base/logging.h>
#include <system/audio_effects/aidl_effects_utils.h>
typedef binder_exception_t (*EffectCreateFunctor)(
const ::aidl::android::media::audio::common::AudioUuid*,
std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>*);
typedef binder_exception_t (*EffectDestroyFunctor)(
const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>&);
typedef binder_exception_t (*EffectQueryFunctor)(
const ::aidl::android::media::audio::common::AudioUuid*,
::aidl::android::hardware::audio::effect::Descriptor*);
struct effect_dl_interface_s {
EffectCreateFunctor createEffectFunc;
EffectDestroyFunctor destroyEffectFunc;
EffectQueryFunctor queryEffectFunc;
};
namespace aidl::android::hardware::audio::effect {
enum class RetCode {
SUCCESS,
ERROR_ILLEGAL_PARAMETER, /* Illegal parameter */
ERROR_THREAD, /* Effect thread error */
ERROR_NULL_POINTER, /* NULL pointer */
ERROR_ALIGNMENT_ERROR, /* Memory alignment error */
ERROR_BLOCK_SIZE_EXCEED, /* Maximum block size exceeded */
ERROR_EFFECT_LIB_ERROR, /* Effect implementation library error */
ERROR_EVENT_FLAG_ERROR /* Error with effect event flags */
};
static const int INVALID_AUDIO_SESSION_ID = -1;
inline std::ostream& operator<<(std::ostream& out, const RetCode& code) {
switch (code) {
case RetCode::SUCCESS:
return out << "SUCCESS";
case RetCode::ERROR_ILLEGAL_PARAMETER:
return out << "ERROR_ILLEGAL_PARAMETER";
case RetCode::ERROR_THREAD:
return out << "ERROR_THREAD";
case RetCode::ERROR_NULL_POINTER:
return out << "ERROR_NULL_POINTER";
case RetCode::ERROR_ALIGNMENT_ERROR:
return out << "ERROR_ALIGNMENT_ERROR";
case RetCode::ERROR_BLOCK_SIZE_EXCEED:
return out << "ERROR_BLOCK_SIZE_EXCEED";
case RetCode::ERROR_EFFECT_LIB_ERROR:
return out << "ERROR_EFFECT_LIB_ERROR";
case RetCode::ERROR_EVENT_FLAG_ERROR:
return out << "ERROR_EVENT_FLAG_ERROR";
}
return out << "EnumError: " << code;
}
#define RETURN_IF_ASTATUS_NOT_OK(status, message) \
do { \
const ::ndk::ScopedAStatus curr_status = (status); \
if (!curr_status.isOk()) { \
LOG(ERROR) << __func__ << ": line" << __LINE__ \
<< " return with status: " << curr_status.getDescription() << (message); \
return ndk::ScopedAStatus::fromExceptionCodeWithMessage( \
curr_status.getExceptionCode(), (message)); \
} \
} while (0)
#define RETURN_IF(expr, exception, message) \
do { \
if (expr) { \
LOG(ERROR) << __func__ << ": line" << __LINE__ << " return with expr " << #expr; \
return ndk::ScopedAStatus::fromExceptionCodeWithMessage((exception), (message)); \
} \
} while (0)
#define RETURN_OK_IF(expr) \
do { \
if (expr) { \
LOG(INFO) << __func__ << ": line" << __LINE__ << " return with expr " << #expr; \
return ndk::ScopedAStatus::ok(); \
} \
} while (0)
#define RETURN_VALUE_IF(expr, ret, log) \
do { \
if (expr) { \
LOG(ERROR) << __func__ << ": line" << __LINE__ << " return with expr \"" << #expr \
<< "\":" << (log); \
return ret; \
} \
} while (0)
#define RETURN_IF_BINDER_EXCEPTION(functor) \
{ \
binder_exception_t exception = functor; \
if (EX_NONE != exception) { \
LOG(ERROR) << #functor << ": failed with error " << exception; \
return ndk::ScopedAStatus::fromExceptionCode(exception); \
} \
}
/**
* Make a Range::$EffectType$Range.
* T: The $EffectType$, Visualizer for example.
* Tag: The union tag name in $EffectType$ definition, latencyMs for example.
* l: The value of Range::$EffectType$Range.min.
* r: The value of Range::$EffectType$Range.max.
*/
#define MAKE_RANGE(T, Tag, l, r) \
{ .min = T::make<T::Tag>(l), .max = T::make<T::Tag>(r) }
} // namespace aidl::android::hardware::audio::effect

View File

@@ -0,0 +1,117 @@
/*
* Copyright (C) 2017 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 <functional>
#include <map>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include <cutils/properties.h>
#include <tinyxml2.h>
#include <aidl/android/hardware/audio/effect/Processing.h>
#include "effect-impl/EffectTypes.h"
namespace aidl::android::hardware::audio::effect {
/**
* Library contains a mapping from library name to path.
* Effect contains a mapping from effect name to Libraries and implementation UUID.
* Pre/post processor contains a mapping from processing name to effect names.
*/
class EffectConfig {
public:
explicit EffectConfig(const std::string& file);
struct Library {
std::string name; // library name
::aidl::android::media::audio::common::AudioUuid uuid; // implementation UUID
std::optional<::aidl::android::media::audio::common::AudioUuid> type; // optional type UUID
};
// <effects>
struct EffectLibraries {
std::optional<struct Library> proxyLibrary;
std::vector<struct Library> libraries;
};
int getSkippedElements() const { return mSkippedElements; }
const std::unordered_map<std::string, std::string> getLibraryMap() const { return mLibraryMap; }
const std::unordered_map<std::string, struct EffectLibraries> getEffectsMap() const {
return mEffectsMap;
}
static bool findUuid(const std::pair<std::string, struct EffectLibraries>& effectElem,
::aidl::android::media::audio::common::AudioUuid* uuid);
using ProcessingLibrariesMap = std::map<Processing::Type, std::vector<struct EffectLibraries>>;
const ProcessingLibrariesMap& getProcessingMap() const;
private:
#ifdef __LP64__
#define SOUND_FX_PATH "/lib64/soundfx/"
#else
#define SOUND_FX_PATH "/lib/soundfx/"
#endif
static constexpr const char* kEffectLibPath[] =
{ "/odm" SOUND_FX_PATH, "/vendor" SOUND_FX_PATH, "/system" SOUND_FX_PATH };
static constexpr const char* kEffectLibApexPath = SOUND_FX_PATH;
#undef SOUND_FX_PATH
int mSkippedElements;
/* Parsed Libraries result */
std::unordered_map<std::string, std::string> mLibraryMap;
/* Parsed Effects result */
std::unordered_map<std::string, struct EffectLibraries> mEffectsMap;
/**
* For parsed pre/post processing result: {key: AudioStreamType/AudioSource/AudioDevice, value:
* EffectLibraries}
*/
ProcessingLibrariesMap mProcessingMap;
/** @return all `node`s children that are elements and match the tag if provided. */
std::vector<std::reference_wrapper<const tinyxml2::XMLElement>> getChildren(
const tinyxml2::XMLNode& node, const char* childTag = nullptr);
/** Parse a library xml note and push the result in mLibraryMap or return false on failure. */
bool parseLibrary(const tinyxml2::XMLElement& xml);
/** Parse an effect from an xml element describing it.
* @return true and pushes the effect in mEffectsMap on success, false on failure.
*/
bool parseEffect(const tinyxml2::XMLElement& xml);
bool parseProcessing(Processing::Type::Tag typeTag, const tinyxml2::XMLElement& xml);
// Function to parse effect.library name and effect.uuid from xml
bool parseLibrary(const tinyxml2::XMLElement& xml, struct Library& library,
bool isProxy = false);
const char* dump(const tinyxml2::XMLElement& element,
tinyxml2::XMLPrinter&& printer = {}) const;
bool resolveLibrary(const std::string& path, std::string* resolvedPath);
std::optional<Processing::Type> stringToProcessingType(Processing::Type::Tag typeTag,
const std::string& type,
const std::string& address);
};
} // namespace aidl::android::hardware::audio::effect

View File

@@ -0,0 +1,125 @@
/*
* 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 <any>
#include <map>
#include <optional>
#include <set>
#include <tuple>
#include <vector>
#include <aidl/android/hardware/audio/effect/BnFactory.h>
#include <android-base/thread_annotations.h>
#include "EffectConfig.h"
namespace aidl::android::hardware::audio::effect {
class Factory : public BnFactory {
public:
explicit Factory(const std::string& file);
/**
* @brief Get identity of all effects supported by the device, with the optional filter by type
* and/or by instance UUID.
*
* @param in_type Type UUID.
* @param in_instance Instance UUID.
* @param in_proxy Proxy UUID.
* @param out_descriptor List of Descriptors.
* @return ndk::ScopedAStatus
*/
ndk::ScopedAStatus queryEffects(
const std::optional<::aidl::android::media::audio::common::AudioUuid>& in_type,
const std::optional<::aidl::android::media::audio::common::AudioUuid>& in_instance,
const std::optional<::aidl::android::media::audio::common::AudioUuid>& in_proxy,
std::vector<Descriptor>* out_descriptor) override;
/**
* @brief Query list of defined processing, with the optional filter by AudioStreamType
*
* @param in_type Type of processing, could be AudioStreamType or AudioSource. Optional.
* @param _aidl_return List of processing filtered by in_type.
* @return ndk::ScopedAStatus
*/
ndk::ScopedAStatus queryProcessing(const std::optional<Processing::Type>& in_type,
std::vector<Processing>* _aidl_return) override;
/**
* @brief Create an effect instance for a certain implementation (identified by UUID).
*
* @param in_impl_uuid Effect implementation UUID.
* @param _aidl_return A pointer to created effect instance.
* @return ndk::ScopedAStatus
*/
ndk::ScopedAStatus createEffect(
const ::aidl::android::media::audio::common::AudioUuid& in_impl_uuid,
std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>* _aidl_return)
override;
/**
* @brief Destroy an effect instance.
*
* @param in_handle Effect instance handle.
* @return ndk::ScopedAStatus
*/
ndk::ScopedAStatus destroyEffect(
const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_handle)
override;
private:
const EffectConfig mConfig;
~Factory();
std::mutex mMutex;
// Set of effect descriptors supported by the devices.
std::set<Descriptor> mDescSet GUARDED_BY(mMutex);
std::set<Descriptor::Identity> mIdentitySet GUARDED_BY(mMutex);
static constexpr int kMapEntryHandleIndex = 0;
static constexpr int kMapEntryInterfaceIndex = 1;
static constexpr int kMapEntryLibNameIndex = 2;
typedef std::tuple<std::unique_ptr<void, std::function<void(void*)>> /* dlHandle */,
std::unique_ptr<struct effect_dl_interface_s> /* interfaces */,
std::string /* library name */>
DlEntry;
std::map<aidl::android::media::audio::common::AudioUuid /* implUUID */, DlEntry> mEffectLibMap
GUARDED_BY(mMutex);
typedef std::pair<aidl::android::media::audio::common::AudioUuid, ndk::SpAIBinder> EffectEntry;
std::map<std::weak_ptr<IEffect>, EffectEntry, std::owner_less<>> mEffectMap GUARDED_BY(mMutex);
ndk::ScopedAStatus destroyEffectImpl_l(const std::shared_ptr<IEffect>& in_handle)
REQUIRES(mMutex);
void cleanupEffectMap_l() REQUIRES(mMutex);
bool openEffectLibrary(const ::aidl::android::media::audio::common::AudioUuid& impl,
const std::string& path);
void createIdentityWithConfig(
const EffectConfig::Library& configLib,
const ::aidl::android::media::audio::common::AudioUuid& typeUuidStr,
const std::optional<::aidl::android::media::audio::common::AudioUuid> proxyUuid);
ndk::ScopedAStatus getDescriptorWithUuid_l(
const aidl::android::media::audio::common::AudioUuid& uuid, Descriptor* desc)
REQUIRES(mMutex);
void loadEffectLibs();
/* Get effect_dl_interface_s from library handle */
void getDlSyms_l(DlEntry& entry) REQUIRES(mMutex);
};
} // namespace aidl::android::hardware::audio::effect