Merge "Camera: Add adb shell cmd to override stream use case"
diff --git a/drm/libmediadrm/CryptoHal.cpp b/drm/libmediadrm/CryptoHal.cpp
index f95d527..fc1780d 100644
--- a/drm/libmediadrm/CryptoHal.cpp
+++ b/drm/libmediadrm/CryptoHal.cpp
@@ -71,7 +71,7 @@
mCryptoHalHidl->notifyResolution(width, height);
}
-status_t CryptoHal::setMediaDrmSession(const Vector<uint8_t>& sessionId) {
+DrmStatus CryptoHal::setMediaDrmSession(const Vector<uint8_t>& sessionId) {
// This requires plugin to be created.
if (mCryptoHalAidl->initCheck() == OK) return mCryptoHalAidl->setMediaDrmSession(sessionId);
return mCryptoHalHidl->setMediaDrmSession(sessionId);
diff --git a/drm/libmediadrm/CryptoHalAidl.cpp b/drm/libmediadrm/CryptoHalAidl.cpp
index f01ecb3..c1fd797 100644
--- a/drm/libmediadrm/CryptoHalAidl.cpp
+++ b/drm/libmediadrm/CryptoHalAidl.cpp
@@ -260,7 +260,7 @@
}
}
-status_t CryptoHalAidl::setMediaDrmSession(const Vector<uint8_t>& sessionId) {
+DrmStatus CryptoHalAidl::setMediaDrmSession(const Vector<uint8_t>& sessionId) {
Mutex::Autolock autoLock(mLock);
if (mInitCheck != OK) {
diff --git a/drm/libmediadrm/CryptoHalHidl.cpp b/drm/libmediadrm/CryptoHalHidl.cpp
index 55364b5..458a1ae 100644
--- a/drm/libmediadrm/CryptoHalHidl.cpp
+++ b/drm/libmediadrm/CryptoHalHidl.cpp
@@ -386,7 +386,7 @@
ALOGE_IF(!hResult.isOk(), "notifyResolution txn failed %s", hResult.description().c_str());
}
-status_t CryptoHalHidl::setMediaDrmSession(const Vector<uint8_t>& sessionId) {
+DrmStatus CryptoHalHidl::setMediaDrmSession(const Vector<uint8_t>& sessionId) {
Mutex::Autolock autoLock(mLock);
if (mInitCheck != OK) {
diff --git a/drm/libmediadrm/include/mediadrm/CryptoHal.h b/drm/libmediadrm/include/mediadrm/CryptoHal.h
index 32a6741..60cbbf8 100644
--- a/drm/libmediadrm/include/mediadrm/CryptoHal.h
+++ b/drm/libmediadrm/include/mediadrm/CryptoHal.h
@@ -38,7 +38,7 @@
virtual bool requiresSecureDecoderComponent(
const char *mime) const;
virtual void notifyResolution(uint32_t width, uint32_t height);
- virtual status_t setMediaDrmSession(const Vector<uint8_t> &sessionId);
+ virtual DrmStatus setMediaDrmSession(const Vector<uint8_t> &sessionId);
virtual ssize_t decrypt(const uint8_t key[16], const uint8_t iv[16],
CryptoPlugin::Mode mode, const CryptoPlugin::Pattern &pattern,
const drm::V1_0::SharedBuffer &source, size_t offset,
diff --git a/drm/libmediadrm/include/mediadrm/CryptoHalAidl.h b/drm/libmediadrm/include/mediadrm/CryptoHalAidl.h
index 50878a6..eec4585 100644
--- a/drm/libmediadrm/include/mediadrm/CryptoHalAidl.h
+++ b/drm/libmediadrm/include/mediadrm/CryptoHalAidl.h
@@ -49,7 +49,7 @@
virtual status_t destroyPlugin();
virtual bool requiresSecureDecoderComponent(const char* mime) const;
virtual void notifyResolution(uint32_t width, uint32_t height);
- virtual status_t setMediaDrmSession(const Vector<uint8_t>& sessionId);
+ virtual DrmStatus setMediaDrmSession(const Vector<uint8_t>& sessionId);
virtual ssize_t decrypt(const uint8_t key[16], const uint8_t iv[16], CryptoPlugin::Mode mode,
const CryptoPlugin::Pattern& pattern, const ::SharedBuffer& source,
size_t offset, const CryptoPlugin::SubSample* subSamples,
diff --git a/drm/libmediadrm/include/mediadrm/CryptoHalHidl.h b/drm/libmediadrm/include/mediadrm/CryptoHalHidl.h
index 6db1e89..0150d7c 100644
--- a/drm/libmediadrm/include/mediadrm/CryptoHalHidl.h
+++ b/drm/libmediadrm/include/mediadrm/CryptoHalHidl.h
@@ -57,7 +57,7 @@
virtual void notifyResolution(uint32_t width, uint32_t height);
- virtual status_t setMediaDrmSession(const Vector<uint8_t> &sessionId);
+ virtual DrmStatus setMediaDrmSession(const Vector<uint8_t> &sessionId);
virtual ssize_t decrypt(const uint8_t key[16], const uint8_t iv[16],
CryptoPlugin::Mode mode, const CryptoPlugin::Pattern &pattern,
diff --git a/drm/libmediadrm/include/mediadrm/DrmStatus.h b/drm/libmediadrm/interface/mediadrm/DrmStatus.h
similarity index 100%
rename from drm/libmediadrm/include/mediadrm/DrmStatus.h
rename to drm/libmediadrm/interface/mediadrm/DrmStatus.h
diff --git a/drm/libmediadrm/interface/mediadrm/ICrypto.h b/drm/libmediadrm/interface/mediadrm/ICrypto.h
index 2c4df60..4087186 100644
--- a/drm/libmediadrm/interface/mediadrm/ICrypto.h
+++ b/drm/libmediadrm/interface/mediadrm/ICrypto.h
@@ -17,6 +17,7 @@
#include <cutils/native_handle.h>
#include <media/hardware/CryptoAPI.h>
#include <media/stagefright/foundation/ABase.h>
+#include <mediadrm/DrmStatus.h>
#include <utils/RefBase.h>
#include <utils/StrongPointer.h>
@@ -65,7 +66,7 @@
virtual void notifyResolution(uint32_t width, uint32_t height) = 0;
- virtual status_t setMediaDrmSession(const Vector<uint8_t> &sessionId) = 0;
+ virtual DrmStatus setMediaDrmSession(const Vector<uint8_t> &sessionId) = 0;
enum DestinationType {
kDestinationTypeSharedMemory, // non-secure
diff --git a/media/audioaidlconversion/AidlConversionCppNdk.cpp b/media/audioaidlconversion/AidlConversionCppNdk.cpp
index 42e4717..6dfcc6b 100644
--- a/media/audioaidlconversion/AidlConversionCppNdk.cpp
+++ b/media/audioaidlconversion/AidlConversionCppNdk.cpp
@@ -29,7 +29,7 @@
#include <media/stagefright/foundation/MediaDefs.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
-// Utilities
+// AIDL CPP/NDK backend to legacy audio data structure conversion utilities.
#if defined(BACKEND_NDK)
/* AIDL String generated in NDK is different than CPP */
@@ -846,8 +846,8 @@
case Tag::indexMask:
// Index masks do not have pre-defined values.
if (const int bits = aidl.get<Tag::indexMask>();
- __builtin_popcount(bits) != 0 &&
- __builtin_popcount(bits) <= AUDIO_CHANNEL_COUNT_MAX) {
+ __builtin_popcount(bits) != 0 &&
+ __builtin_popcount(bits) <= (int)AUDIO_CHANNEL_COUNT_MAX) {
return audio_channel_mask_from_representation_and_bits(
AUDIO_CHANNEL_REPRESENTATION_INDEX, bits);
} else {
@@ -1123,7 +1123,7 @@
for (size_t i = 0; i < numValues; ++i) {
legacy.values[i] = VALUE_OR_RETURN(convertIntegral<int>(aidl.values[i]));
}
- legacy.ramp_duration_ms = VALUE_OR_RETURN(convertIntegral<unsigned int>(aidl.rampDurationMs));
+ legacy.ramp_duration_ms = VALUE_OR_RETURN(convertIntegral<int>(aidl.rampDurationMs));
return legacy;
}
@@ -1167,6 +1167,10 @@
return AUDIO_INPUT_FLAG_DIRECT;
case AudioInputFlags::ULTRASOUND:
return AUDIO_INPUT_FLAG_ULTRASOUND;
+ case AudioInputFlags::HOTWORD_TAP:
+ return AUDIO_INPUT_FLAG_HOTWORD_TAP;
+ case AudioInputFlags::HW_LOOKBACK:
+ return AUDIO_INPUT_FLAG_HW_LOOKBACK;
}
return unexpected(BAD_VALUE);
}
@@ -1194,6 +1198,10 @@
return AudioInputFlags::DIRECT;
case AUDIO_INPUT_FLAG_ULTRASOUND:
return AudioInputFlags::ULTRASOUND;
+ case AUDIO_INPUT_FLAG_HOTWORD_TAP:
+ return AudioInputFlags::HOTWORD_TAP;
+ case AUDIO_INPUT_FLAG_HW_LOOKBACK:
+ return AudioInputFlags::HW_LOOKBACK;
}
return unexpected(BAD_VALUE);
}
@@ -1710,12 +1718,12 @@
legacy.format = base.format;
legacy.stream_type = VALUE_OR_RETURN(
aidl2legacy_AudioStreamType_audio_stream_type_t(aidl.streamType));
- legacy.bit_rate = VALUE_OR_RETURN(convertIntegral<uint32_t>(aidl.bitRatePerSecond));
+ legacy.bit_rate = VALUE_OR_RETURN(convertIntegral<int32_t>(aidl.bitRatePerSecond));
legacy.duration_us = VALUE_OR_RETURN(convertIntegral<int64_t>(aidl.durationUs));
legacy.has_video = aidl.hasVideo;
legacy.is_streaming = aidl.isStreaming;
- legacy.bit_width = VALUE_OR_RETURN(convertIntegral<uint32_t>(aidl.bitWidth));
- legacy.offload_buffer_size = VALUE_OR_RETURN(convertIntegral<uint32_t>(aidl.offloadBufferSize));
+ legacy.bit_width = VALUE_OR_RETURN(convertIntegral<int32_t>(aidl.bitWidth));
+ legacy.offload_buffer_size = VALUE_OR_RETURN(convertIntegral<int32_t>(aidl.offloadBufferSize));
legacy.usage = VALUE_OR_RETURN(aidl2legacy_AudioUsage_audio_usage_t(aidl.usage));
legacy.encapsulation_mode = VALUE_OR_RETURN(
aidl2legacy_AudioEncapsulationMode_audio_encapsulation_mode_t(aidl.encapsulationMode));
@@ -1789,7 +1797,7 @@
ConversionResult<audio_config_base_t>
aidl2legacy_AudioConfigBase_audio_config_base_t(const AudioConfigBase& aidl, bool isInput) {
audio_config_base_t legacy;
- legacy.sample_rate = VALUE_OR_RETURN(convertIntegral<uint32_t>(aidl.sampleRate));
+ legacy.sample_rate = VALUE_OR_RETURN(convertIntegral<int>(aidl.sampleRate));
legacy.channel_mask = VALUE_OR_RETURN(
aidl2legacy_AudioChannelLayout_audio_channel_mask_t(aidl.channelMask, isInput));
legacy.format = VALUE_OR_RETURN(aidl2legacy_AudioFormatDescription_audio_format_t(aidl.format));
@@ -2271,4 +2279,4 @@
#if defined(BACKEND_NDK)
} // aidl
-#endif
\ No newline at end of file
+#endif
diff --git a/media/audioaidlconversion/AidlConversionNdk.cpp b/media/audioaidlconversion/AidlConversionNdk.cpp
new file mode 100644
index 0000000..a3e39c7
--- /dev/null
+++ b/media/audioaidlconversion/AidlConversionNdk.cpp
@@ -0,0 +1,259 @@
+/*
+ * 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.
+ */
+
+#include <utility>
+
+#define LOG_TAG "AidlConversionNdk"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <media/AidlConversionCppNdk.h>
+#include <media/AidlConversionNdk.h>
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// AIDL NDK backend to legacy audio data structure conversion utilities.
+
+namespace aidl {
+namespace android {
+
+using ::aidl::android::hardware::audio::effect::Descriptor;
+using ::aidl::android::hardware::audio::effect::Flags;
+
+using ::android::BAD_VALUE;
+using ::android::base::unexpected;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Converters
+
+ConversionResult<uint32_t> aidl2legacy_Flags_Type_uint32(Flags::Type type) {
+ switch (type) {
+ case Flags::Type::INSERT:
+ return EFFECT_FLAG_TYPE_INSERT;
+ case Flags::Type::AUXILIARY:
+ return EFFECT_FLAG_TYPE_AUXILIARY;
+ case Flags::Type::REPLACE:
+ return EFFECT_FLAG_TYPE_REPLACE;
+ case Flags::Type::PRE_PROC:
+ return EFFECT_FLAG_TYPE_PRE_PROC;
+ case Flags::Type::POST_PROC:
+ return EFFECT_FLAG_TYPE_POST_PROC;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<uint32_t> aidl2legacy_Flags_Insert_uint32(Flags::Insert insert) {
+ switch (insert) {
+ case Flags::Insert::ANY:
+ return EFFECT_FLAG_INSERT_ANY;
+ case Flags::Insert::FIRST:
+ return EFFECT_FLAG_INSERT_FIRST;
+ case Flags::Insert::LAST:
+ return EFFECT_FLAG_INSERT_LAST;
+ case Flags::Insert::EXCLUSIVE:
+ return EFFECT_FLAG_INSERT_EXCLUSIVE;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<uint32_t> aidl2legacy_Flags_Volume_uint32(Flags::Volume volume) {
+ switch (volume) {
+ case Flags::Volume::NONE:
+ return 0;
+ case Flags::Volume::CTRL:
+ return EFFECT_FLAG_VOLUME_CTRL;
+ case Flags::Volume::IND:
+ return EFFECT_FLAG_VOLUME_IND;
+ case Flags::Volume::MONITOR:
+ return EFFECT_FLAG_VOLUME_MONITOR;
+ }
+ return unexpected(BAD_VALUE);
+}
+ConversionResult<uint32_t> aidl2legacy_Flags_HardwareAccelerator_uint32(
+ Flags::HardwareAccelerator hwAcceleratorMode) {
+ switch (hwAcceleratorMode) {
+ case Flags::HardwareAccelerator::NONE:
+ return 0;
+ case Flags::HardwareAccelerator::SIMPLE:
+ return EFFECT_FLAG_HW_ACC_SIMPLE;
+ case Flags::HardwareAccelerator::TUNNEL:
+ return EFFECT_FLAG_HW_ACC_TUNNEL;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<uint32_t> aidl2legacy_Flags_uint32(Flags aidl) {
+ uint32_t legacy = 0;
+ legacy |= VALUE_OR_RETURN(aidl2legacy_Flags_Type_uint32(aidl.type));
+ legacy |= VALUE_OR_RETURN(aidl2legacy_Flags_Insert_uint32(aidl.insert));
+ legacy |= VALUE_OR_RETURN(aidl2legacy_Flags_Volume_uint32(aidl.volume));
+ legacy |= VALUE_OR_RETURN(aidl2legacy_Flags_HardwareAccelerator_uint32(aidl.hwAcceleratorMode));
+
+ if (aidl.offloadIndication) {
+ legacy |= EFFECT_FLAG_OFFLOAD_SUPPORTED;
+ }
+ if (aidl.deviceIndication) {
+ legacy |= EFFECT_FLAG_DEVICE_IND;
+ }
+ if (aidl.audioModeIndication) {
+ legacy |= EFFECT_FLAG_AUDIO_MODE_IND;
+ }
+ if (aidl.audioSourceIndication) {
+ legacy |= EFFECT_FLAG_AUDIO_SOURCE_IND;
+ }
+ if (aidl.noProcessing) {
+ legacy |= EFFECT_FLAG_NO_PROCESS;
+ }
+ return legacy;
+}
+
+ConversionResult<Flags::Type> legacy2aidl_uint32_Flags_Type(uint32_t legacy) {
+ switch (legacy & EFFECT_FLAG_TYPE_MASK) {
+ case EFFECT_FLAG_TYPE_INSERT:
+ return Flags::Type::INSERT;
+ case EFFECT_FLAG_TYPE_AUXILIARY:
+ return Flags::Type::AUXILIARY;
+ case EFFECT_FLAG_TYPE_REPLACE:
+ return Flags::Type::REPLACE;
+ case EFFECT_FLAG_TYPE_PRE_PROC:
+ return Flags::Type::PRE_PROC;
+ case EFFECT_FLAG_TYPE_POST_PROC:
+ return Flags::Type::POST_PROC;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<Flags::Insert> legacy2aidl_uint32_Flags_Insert(uint32_t legacy) {
+ switch (legacy & EFFECT_FLAG_INSERT_MASK) {
+ case EFFECT_FLAG_INSERT_ANY:
+ return Flags::Insert::ANY;
+ case EFFECT_FLAG_INSERT_FIRST:
+ return Flags::Insert::FIRST;
+ case EFFECT_FLAG_INSERT_LAST:
+ return Flags::Insert::LAST;
+ case EFFECT_FLAG_INSERT_EXCLUSIVE:
+ return Flags::Insert::EXCLUSIVE;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<Flags::Volume> legacy2aidl_uint32_Flags_Volume(uint32_t legacy) {
+ switch (legacy & EFFECT_FLAG_VOLUME_MASK) {
+ case EFFECT_FLAG_VOLUME_IND:
+ return Flags::Volume::IND;
+ case EFFECT_FLAG_VOLUME_MONITOR:
+ return Flags::Volume::MONITOR;
+ case EFFECT_FLAG_VOLUME_NONE:
+ return Flags::Volume::NONE;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<Flags::HardwareAccelerator> legacy2aidl_uint32_Flags_HardwareAccelerator(
+ uint32_t legacy) {
+ switch (legacy & EFFECT_FLAG_HW_ACC_MASK) {
+ case EFFECT_FLAG_HW_ACC_SIMPLE:
+ return Flags::HardwareAccelerator::SIMPLE;
+ case EFFECT_FLAG_HW_ACC_TUNNEL:
+ return Flags::HardwareAccelerator::TUNNEL;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<Flags> legacy2aidl_uint32_Flags(uint32_t legacy) {
+ Flags aidl;
+
+ aidl.type = VALUE_OR_RETURN(legacy2aidl_uint32_Flags_Type(legacy));
+ aidl.insert = VALUE_OR_RETURN(legacy2aidl_uint32_Flags_Insert(legacy));
+ aidl.volume = VALUE_OR_RETURN(legacy2aidl_uint32_Flags_Volume(legacy));
+ aidl.hwAcceleratorMode = VALUE_OR_RETURN(legacy2aidl_uint32_Flags_HardwareAccelerator(legacy));
+ aidl.offloadIndication = (legacy & EFFECT_FLAG_OFFLOAD_SUPPORTED);
+ aidl.deviceIndication = (legacy & EFFECT_FLAG_DEVICE_IND);
+ aidl.audioModeIndication = (legacy & EFFECT_FLAG_AUDIO_MODE_IND);
+ aidl.audioSourceIndication = (legacy & EFFECT_FLAG_AUDIO_SOURCE_IND);
+ aidl.noProcessing = (legacy & EFFECT_FLAG_NO_PROCESS);
+ return aidl;
+}
+
+ConversionResult<effect_descriptor_t>
+aidl2legacy_Descriptor_effect_descriptor(const Descriptor& aidl) {
+ effect_descriptor_t legacy;
+ legacy.type = VALUE_OR_RETURN(aidl2legacy_AudioUuid_audio_uuid_t(aidl.common.id.type));
+ legacy.uuid = VALUE_OR_RETURN(aidl2legacy_AudioUuid_audio_uuid_t(aidl.common.id.uuid));
+ // legacy descriptor doesn't have proxy information
+ // proxy = VALUE_OR_RETURN(aidl2legacy_AudioUuid_audio_uuid_t(aidl.proxy));
+ legacy.apiVersion = EFFECT_CONTROL_API_VERSION;
+ legacy.flags = VALUE_OR_RETURN(aidl2legacy_Flags_uint32(aidl.common.flags));
+ legacy.cpuLoad = VALUE_OR_RETURN(convertIntegral<uint16_t>(aidl.common.cpuLoad));
+ legacy.memoryUsage = VALUE_OR_RETURN(convertIntegral<uint16_t>(aidl.common.memoryUsage));
+ RETURN_IF_ERROR(aidl2legacy_string(aidl.common.name, legacy.name, sizeof(legacy.name)));
+ RETURN_IF_ERROR(aidl2legacy_string(aidl.common.implementor, legacy.implementor,
+ sizeof(legacy.implementor)));
+ return legacy;
+}
+
+ConversionResult<Descriptor>
+legacy2aidl_effect_descriptor_Descriptor(const effect_descriptor_t& legacy) {
+ Descriptor aidl;
+ aidl.common.id.type = VALUE_OR_RETURN(legacy2aidl_audio_uuid_t_AudioUuid(legacy.type));
+ aidl.common.id.uuid = VALUE_OR_RETURN(legacy2aidl_audio_uuid_t_AudioUuid(legacy.uuid));
+ // legacy descriptor doesn't have proxy information
+ // aidl.common.id.proxy
+ aidl.common.flags = VALUE_OR_RETURN(legacy2aidl_uint32_Flags(legacy.flags));
+ aidl.common.cpuLoad = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.cpuLoad));
+ aidl.common.memoryUsage = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.memoryUsage));
+ aidl.common.name = VALUE_OR_RETURN(legacy2aidl_string(legacy.name, sizeof(legacy.name)));
+ aidl.common.implementor =
+ VALUE_OR_RETURN(legacy2aidl_string(legacy.implementor, sizeof(legacy.implementor)));
+ return aidl;
+}
+
+ConversionResult<buffer_config_t> aidl2legacy_AudioConfigBase_buffer_config_t(
+ const media::audio::common::AudioConfigBase& aidl, bool isInput) {
+ buffer_config_t legacy;
+
+ legacy.samplingRate = VALUE_OR_RETURN(convertIntegral<uint32_t>(aidl.sampleRate));
+ legacy.mask |= EFFECT_CONFIG_SMP_RATE;
+
+ legacy.channels = VALUE_OR_RETURN(
+ aidl2legacy_AudioChannelLayout_audio_channel_mask_t(aidl.channelMask, isInput));
+ legacy.mask |= EFFECT_CONFIG_CHANNELS;
+
+ legacy.format = VALUE_OR_RETURN(aidl2legacy_AudioFormatDescription_audio_format_t(aidl.format));
+ legacy.mask |= EFFECT_CONFIG_FORMAT;
+
+ return legacy;
+}
+
+ConversionResult<media::audio::common::AudioConfigBase>
+legacy2aidl_AudioConfigBase_buffer_config_t(const buffer_config_t& legacy, bool isInput) {
+ media::audio::common::AudioConfigBase aidl;
+
+ if (legacy.mask & EFFECT_CONFIG_SMP_RATE) {
+ aidl.sampleRate = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.samplingRate));
+ }
+ if (legacy.mask & EFFECT_CONFIG_CHANNELS) {
+ aidl.channelMask = VALUE_OR_RETURN(legacy2aidl_audio_channel_mask_t_AudioChannelLayout(
+ static_cast<audio_channel_mask_t>(legacy.channels), isInput));
+ }
+ if (legacy.mask & EFFECT_CONFIG_FORMAT) {
+ aidl.format = VALUE_OR_RETURN(legacy2aidl_audio_format_t_AudioFormatDescription(
+ static_cast<audio_format_t>(legacy.format)));
+ }
+ return aidl;
+}
+
+} // namespace android
+} // aidl
diff --git a/media/audioaidlconversion/Android.bp b/media/audioaidlconversion/Android.bp
index 4a1df3a..86f455e 100644
--- a/media/audioaidlconversion/Android.bp
+++ b/media/audioaidlconversion/Android.bp
@@ -61,9 +61,9 @@
host_supported: true,
vendor_available: true,
double_loadable: true,
- min_sdk_version: "29",
header_libs: [
"libaudio_system_headers",
+ "libhardware_headers",
],
shared_libs: [
"libbase",
@@ -102,7 +102,9 @@
*/
cc_library {
name: "libaudio_aidl_conversion_common_cpp",
- srcs: ["AidlConversionCppNdk.cpp"],
+ srcs: [
+ "AidlConversionCppNdk.cpp",
+ ],
header_libs: [
"libaudio_aidl_conversion_common_util_cpp",
],
@@ -113,6 +115,7 @@
"audio_aidl_conversion_common_default",
"latest_android_media_audio_common_types_cpp_export_shared",
],
+ min_sdk_version: "29",
}
/**
@@ -120,7 +123,10 @@
*/
cc_library {
name: "libaudio_aidl_conversion_common_ndk",
- srcs: ["AidlConversionCppNdk.cpp"],
+ srcs: [
+ "AidlConversionCppNdk.cpp",
+ "AidlConversionNdk.cpp",
+ ],
header_libs: [
"libaudio_aidl_conversion_common_util_ndk",
],
@@ -129,12 +135,16 @@
],
defaults: [
"audio_aidl_conversion_common_default",
+ "latest_android_hardware_audio_common_ndk_shared",
+ "latest_android_hardware_audio_effect_ndk_shared",
"latest_android_media_audio_common_types_ndk_shared",
],
shared_libs: [
"libbinder_ndk",
+ "libbase",
],
cflags: [
"-DBACKEND_NDK",
],
+ min_sdk_version: "31", //AParcelableHolder has been introduced in 31
}
diff --git a/media/audioaidlconversion/include/media/AidlConversionNdk.h b/media/audioaidlconversion/include/media/AidlConversionNdk.h
new file mode 100644
index 0000000..a3176f6
--- /dev/null
+++ b/media/audioaidlconversion/include/media/AidlConversionNdk.h
@@ -0,0 +1,68 @@
+/*
+ * 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_auto_utils.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+/**
+ * Can only handle conversion between AIDL (NDK backend) and legacy type.
+ */
+#include <hardware/audio_effect.h>
+#include <media/AidlConversionUtil.h>
+#include <system/audio_effect.h>
+
+#include <aidl/android/hardware/audio/effect/IEffect.h>
+
+namespace aidl {
+namespace android {
+
+ConversionResult<uint32_t> aidl2legacy_Flags_Type_uint32(
+ ::aidl::android::hardware::audio::effect::Flags::Type type);
+ConversionResult<uint32_t> aidl2legacy_Flags_Insert_uint32(
+ ::aidl::android::hardware::audio::effect::Flags::Insert insert);
+ConversionResult<uint32_t> aidl2legacy_Flags_Volume_uint32(
+ ::aidl::android::hardware::audio::effect::Flags::Volume volume);
+ConversionResult<uint32_t> aidl2legacy_Flags_HardwareAccelerator_uint32(
+ ::aidl::android::hardware::audio::effect::Flags::HardwareAccelerator hwAcceleratorMode);
+ConversionResult<uint32_t> aidl2legacy_Flags_uint32(
+ const ::aidl::android::hardware::audio::effect::Flags aidl);
+
+ConversionResult<::aidl::android::hardware::audio::effect::Flags::Type>
+legacy2aidl_uint32_Flags_Type(uint32_t legacy);
+ConversionResult<::aidl::android::hardware::audio::effect::Flags::Insert>
+legacy2aidl_uint32_Flags_Insert(uint32_t legacy);
+ConversionResult<::aidl::android::hardware::audio::effect::Flags::Volume>
+legacy2aidl_uint32_Flags_Volume(uint32_t legacy);
+ConversionResult<::aidl::android::hardware::audio::effect::Flags::HardwareAccelerator>
+legacy2aidl_uint32_Flags_HardwareAccelerator(uint32_t legacy);
+ConversionResult<::aidl::android::hardware::audio::effect::Flags> legacy2aidl_uint32_Flags(
+ uint32_t hal);
+
+ConversionResult<effect_descriptor_t> aidl2legacy_Descriptor_effect_descriptor(
+ const ::aidl::android::hardware::audio::effect::Descriptor& aidl);
+ConversionResult<::aidl::android::hardware::audio::effect::Descriptor>
+legacy2aidl_effect_descriptor_Descriptor(const effect_descriptor_t& hal);
+
+ConversionResult<buffer_config_t> aidl2legacy_AudioConfigBase_buffer_config_t(
+ const media::audio::common::AudioConfigBase& aidl, bool isInput);
+ConversionResult<media::audio::common::AudioConfigBase> legacy2aidl_AudioConfigBase_buffer_config_t(
+ const buffer_config_t& legacy, bool isInput);
+
+} // namespace android
+} // namespace aidl
diff --git a/media/audioaidlconversion/include/media/AidlConversionUtil.h b/media/audioaidlconversion/include/media/AidlConversionUtil.h
index 95daa4a..28c7522 100644
--- a/media/audioaidlconversion/include/media/AidlConversionUtil.h
+++ b/media/audioaidlconversion/include/media/AidlConversionUtil.h
@@ -25,7 +25,9 @@
#include <error/Result.h>
#if defined(BACKEND_NDK)
+#include <android/binder_enums.h>
#include <android/binder_status.h>
+
namespace aidl {
#else
#include <binder/Enums.h>
diff --git a/media/audioserver/main_audioserver.cpp b/media/audioserver/main_audioserver.cpp
index e3db5b4..1e3bfe0 100644
--- a/media/audioserver/main_audioserver.cpp
+++ b/media/audioserver/main_audioserver.cpp
@@ -50,6 +50,8 @@
int main(int argc __unused, char **argv)
{
+ ALOGD("%s: starting", __func__);
+ const auto startTime = std::chrono::steady_clock::now();
// TODO: update with refined parameters
limitProcessMemory(
"audio.maxmem", /* "ro.audio.maxmem", property that defines limit */
@@ -144,11 +146,36 @@
setpgid(0, 0); // but if I die first, don't kill my parent
}
android::hardware::configureRpcThreadpool(4, false /*callerWillJoin*/);
- sp<ProcessState> proc(ProcessState::self());
+
+ // Ensure threads for possible callbacks. Note that get_audio_flinger() does
+ // this automatically when called from AudioPolicy, but we do this anyways here.
+ ProcessState::self()->startThreadPool();
+
+ // Instantiating AudioFlinger (making it public, e.g. through ::initialize())
+ // and then instantiating AudioPolicy (and making it public)
+ // leads to situations where AudioFlinger is accessed remotely before
+ // AudioPolicy is initialized. Not only might this
+ // cause inaccurate results, but if AudioPolicy has slow audio HAL
+ // initialization, it can cause a TimeCheck abort to occur on an AudioFlinger
+ // call which tries to access AudioPolicy.
+ //
+ // We create AudioFlinger and AudioPolicy locally then make it public to ServiceManager.
+ // This requires both AudioFlinger and AudioPolicy to be in-proc.
+ //
+ const auto af = sp<AudioFlinger>::make();
+ const auto afAdapter = sp<AudioFlingerServerAdapter>::make(af);
+ ALOGD("%s: AudioFlinger created", __func__);
+ ALOGW_IF(AudioSystem::setLocalAudioFlinger(af) != OK,
+ "%s: AudioSystem already has an AudioFlinger instance!", __func__);
+ const auto aps = sp<AudioPolicyService>::make();
+ ALOGD("%s: AudioPolicy created", __func__);
+
+ // Add AudioFlinger and AudioPolicy to ServiceManager.
sp<IServiceManager> sm = defaultServiceManager();
- ALOGI("ServiceManager: %p", sm.get());
- AudioFlinger::instantiate();
- AudioPolicyService::instantiate();
+ sm->addService(String16(IAudioFlinger::DEFAULT_SERVICE_NAME), afAdapter,
+ false /* allowIsolated */, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);
+ sm->addService(String16(AudioPolicyService::getServiceName()), aps,
+ false /* allowIsolated */, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);
// AAudioService should only be used in OC-MR1 and later.
// And only enable the AAudioService if the system MMAP policy explicitly allows it.
@@ -156,7 +183,6 @@
// If we cannot get audio flinger here, there must be some serious problems. In that case,
// attempting to call audio flinger on a null pointer could make the process crash
// and attract attentions.
- sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
std::vector<AudioMMapPolicyInfo> policyInfos;
status_t status = af->getMmapPolicyInfos(
AudioMMapPolicyType::DEFAULT, &policyInfos);
@@ -169,11 +195,14 @@
})) {
AAudioService::instantiate();
} else {
- ALOGD("Do not init aaudio service, status %d, policy info size %zu",
- status, policyInfos.size());
+ ALOGD("%s: Do not init aaudio service, status %d, policy info size %zu",
+ __func__, status, policyInfos.size());
}
-
- ProcessState::self()->startThreadPool();
+ const auto endTime = std::chrono::steady_clock::now();
+ using FloatMillis = std::chrono::duration<float, std::milli>;
+ const float timeTaken = std::chrono::duration_cast<FloatMillis>(
+ endTime - startTime).count();
+ ALOGI("%s: initialization done in %.3f ms, joining thread pool", __func__, timeTaken);
IPCThreadState::self()->joinThreadPool();
}
}
diff --git a/media/libaaudio/fuzzer/Android.bp b/media/libaaudio/fuzzer/Android.bp
index f5d2939..3393930 100644
--- a/media/libaaudio/fuzzer/Android.bp
+++ b/media/libaaudio/fuzzer/Android.bp
@@ -39,6 +39,7 @@
"libaudiomanager",
"libaudiopolicy",
"libaudioclient_aidl_conversion",
+ "libaudio_aidl_conversion_common_cpp",
"libutils",
],
static_libs: [
diff --git a/media/libaaudio/tests/test_attributes.cpp b/media/libaaudio/tests/test_attributes.cpp
index b88d562..e5676a7 100644
--- a/media/libaaudio/tests/test_attributes.cpp
+++ b/media/libaaudio/tests/test_attributes.cpp
@@ -20,6 +20,7 @@
// "test_aaudio_attributes.cpp". That other file is more current.
// So these tests could be deleted.
+#include <memory>
#include <stdio.h>
#include <unistd.h>
@@ -40,7 +41,7 @@
int privacyMode = DONT_SET,
aaudio_direction_t direction = AAUDIO_DIRECTION_OUTPUT) {
- float *buffer = new float[kNumFrames * kChannelCount];
+ std::unique_ptr<float[]> buffer(new float[kNumFrames * kChannelCount]);
AAudioStreamBuilder *aaudioBuilder = nullptr;
AAudioStream *aaudioStream = nullptr;
@@ -109,16 +110,15 @@
if (direction == AAUDIO_DIRECTION_INPUT) {
EXPECT_EQ(kNumFrames,
- AAudioStream_read(aaudioStream, buffer, kNumFrames, kNanosPerSecond));
+ AAudioStream_read(aaudioStream, buffer.get(), kNumFrames, kNanosPerSecond));
} else {
EXPECT_EQ(kNumFrames,
- AAudioStream_write(aaudioStream, buffer, kNumFrames, kNanosPerSecond));
+ AAudioStream_write(aaudioStream, buffer.get(), kNumFrames, kNanosPerSecond));
}
EXPECT_EQ(AAUDIO_OK, AAudioStream_requestStop(aaudioStream));
EXPECT_EQ(AAUDIO_OK, AAudioStream_close(aaudioStream));
- delete[] buffer;
}
static const aaudio_usage_t sUsages[] = {
diff --git a/media/libaaudio/tests/test_recovery.cpp b/media/libaaudio/tests/test_recovery.cpp
index 6e89f83..11331af 100644
--- a/media/libaaudio/tests/test_recovery.cpp
+++ b/media/libaaudio/tests/test_recovery.cpp
@@ -16,6 +16,7 @@
// Play silence and recover from dead servers or disconnected devices.
+#include <memory>
#include <stdio.h>
#include <aaudio/AAudio.h>
@@ -32,7 +33,6 @@
int32_t triesLeft = 3;
int32_t bufferCapacity;
int32_t framesPerBurst = 0;
- float *buffer = nullptr;
int32_t actualChannelCount = 0;
int32_t actualSampleRate = 0;
@@ -83,7 +83,7 @@
bufferCapacity, framesPerBurst);
int samplesPerBurst = framesPerBurst * actualChannelCount;
- buffer = new float[samplesPerBurst];
+ std::unique_ptr<float[]> buffer(new float[samplesPerBurst]);
result = AAudioStream_requestStart(aaudioStream);
if (result != AAUDIO_OK) {
@@ -98,7 +98,7 @@
int64_t printAt = actualSampleRate;
while (result == AAUDIO_OK && framesTotal < framesMax) {
int32_t framesWritten = AAudioStream_write(aaudioStream,
- buffer, framesPerBurst,
+ buffer.get(), framesPerBurst,
DEFAULT_TIMEOUT_NANOS);
if (framesWritten < 0) {
result = framesWritten;
@@ -134,6 +134,5 @@
AAudioStream_close(aaudioStream);
}
AAudioStreamBuilder_delete(aaudioBuilder);
- delete[] buffer;
printf(" result = %d = %s\n", result, AAudio_convertResultToText(result));
}
diff --git a/media/libaaudio/tests/test_session_id.cpp b/media/libaaudio/tests/test_session_id.cpp
index 3f7d4fc..5968b5d 100644
--- a/media/libaaudio/tests/test_session_id.cpp
+++ b/media/libaaudio/tests/test_session_id.cpp
@@ -16,6 +16,7 @@
// Test AAudio SessionId, which is used to associate Effects with a stream
+#include <memory>
#include <stdio.h>
#include <unistd.h>
@@ -29,7 +30,7 @@
// Test AAUDIO_SESSION_ID_NONE default
static void checkSessionIdNone(aaudio_performance_mode_t perfMode) {
- float *buffer = new float[kNumFrames * kChannelCount];
+ std::unique_ptr<float[]> buffer(new float[kNumFrames * kChannelCount]);
AAudioStreamBuilder *aaudioBuilder = nullptr;
@@ -51,12 +52,12 @@
ASSERT_EQ(AAUDIO_OK, AAudioStream_requestStart(aaudioStream1));
- ASSERT_EQ(kNumFrames, AAudioStream_write(aaudioStream1, buffer, kNumFrames, kNanosPerSecond));
+ ASSERT_EQ(kNumFrames,
+ AAudioStream_write(aaudioStream1, buffer.get(), kNumFrames, kNanosPerSecond));
EXPECT_EQ(AAUDIO_OK, AAudioStream_requestStop(aaudioStream1));
EXPECT_EQ(AAUDIO_OK, AAudioStream_close(aaudioStream1));
- delete[] buffer;
AAudioStreamBuilder_delete(aaudioBuilder);
}
@@ -72,7 +73,7 @@
static void checkSessionIdAllocate(aaudio_performance_mode_t perfMode,
aaudio_direction_t direction) {
- float *buffer = new float[kNumFrames * kChannelCount];
+ std::unique_ptr<float[]> buffer(new float[kNumFrames * kChannelCount]);
AAudioStreamBuilder *aaudioBuilder = nullptr;
@@ -106,10 +107,10 @@
if (direction == AAUDIO_DIRECTION_INPUT) {
ASSERT_EQ(kNumFrames, AAudioStream_read(aaudioStream1,
- buffer, kNumFrames, kNanosPerSecond));
+ buffer.get(), kNumFrames, kNanosPerSecond));
} else {
ASSERT_EQ(kNumFrames, AAudioStream_write(aaudioStream1,
- buffer, kNumFrames, kNanosPerSecond));
+ buffer.get(), kNumFrames, kNanosPerSecond));
}
EXPECT_EQ(AAUDIO_OK, AAudioStream_requestStop(aaudioStream1));
@@ -135,10 +136,10 @@
if (otherDirection == AAUDIO_DIRECTION_INPUT) {
ASSERT_EQ(kNumFrames, AAudioStream_read(aaudioStream2,
- buffer, kNumFrames, kNanosPerSecond));
+ buffer.get(), kNumFrames, kNanosPerSecond));
} else {
ASSERT_EQ(kNumFrames, AAudioStream_write(aaudioStream2,
- buffer, kNumFrames, kNanosPerSecond));
+ buffer.get(), kNumFrames, kNanosPerSecond));
}
EXPECT_EQ(AAUDIO_OK, AAudioStream_requestStop(aaudioStream2));
@@ -147,7 +148,6 @@
EXPECT_EQ(AAUDIO_OK, AAudioStream_close(aaudioStream1));
- delete[] buffer;
AAudioStreamBuilder_delete(aaudioBuilder);
}
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index 9ddae1e..50e9ea7 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -128,6 +128,7 @@
"audiopolicy-types-aidl-cpp",
"av-types-aidl-cpp",
"capture_state_listener-aidl-cpp",
+ "libaudio_aidl_conversion_common_cpp",
"libaudioclient_aidl_conversion",
"libaudiofoundation",
"libaudioutils",
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index ad03711..8b7a63a 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -82,7 +82,7 @@
// Binder for the AudioFlinger service that's passed to this client process from the system server.
// This allows specific isolated processes to access the audio system. Currently used only for the
// HotwordDetectionService.
-sp<IBinder> gAudioFlingerBinder = nullptr;
+static sp<IBinder> gAudioFlingerBinder = nullptr;
void AudioSystem::setAudioFlingerBinder(const sp<IBinder>& audioFlinger) {
if (audioFlinger->getInterfaceDescriptor() != media::IAudioFlingerService::descriptor) {
@@ -98,6 +98,15 @@
gAudioFlingerBinder = audioFlinger;
}
+static sp<IAudioFlinger> gLocalAudioFlinger; // set if we are local.
+
+status_t AudioSystem::setLocalAudioFlinger(const sp<IAudioFlinger>& af) {
+ Mutex::Autolock _l(gLock);
+ if (gAudioFlinger != nullptr) return INVALID_OPERATION;
+ gLocalAudioFlinger = af;
+ return OK;
+}
+
// establish binder interface to AudioFlinger service
const sp<IAudioFlinger> AudioSystem::get_audio_flinger() {
sp<IAudioFlinger> af;
@@ -105,7 +114,19 @@
bool reportNoError = false;
{
Mutex::Autolock _l(gLock);
- if (gAudioFlinger == 0) {
+ if (gAudioFlinger != nullptr) {
+ return gAudioFlinger;
+ }
+
+ if (gAudioFlingerClient == nullptr) {
+ gAudioFlingerClient = sp<AudioFlingerClient>::make();
+ } else {
+ reportNoError = true;
+ }
+
+ if (gLocalAudioFlinger != nullptr) {
+ gAudioFlinger = gLocalAudioFlinger;
+ } else {
sp<IBinder> binder;
if (gAudioFlingerBinder != nullptr) {
binder = gAudioFlingerBinder;
@@ -113,32 +134,24 @@
sp<IServiceManager> sm = defaultServiceManager();
do {
binder = sm->getService(String16(IAudioFlinger::DEFAULT_SERVICE_NAME));
- if (binder != 0)
- break;
+ if (binder != nullptr) break;
ALOGW("AudioFlinger not published, waiting...");
usleep(500000); // 0.5 s
} while (true);
}
- if (gAudioFlingerClient == NULL) {
- gAudioFlingerClient = new AudioFlingerClient();
- } else {
- reportNoError = true;
- }
binder->linkToDeath(gAudioFlingerClient);
- gAudioFlinger = new AudioFlingerClientAdapter(
- interface_cast<media::IAudioFlingerService>(binder));
- LOG_ALWAYS_FATAL_IF(gAudioFlinger == 0);
- afc = gAudioFlingerClient;
- // Make sure callbacks can be received by gAudioFlingerClient
- ProcessState::self()->startThreadPool();
+ const auto afs = interface_cast<media::IAudioFlingerService>(binder);
+ LOG_ALWAYS_FATAL_IF(afs == nullptr);
+ gAudioFlinger = sp<AudioFlingerClientAdapter>::make(afs);
}
+ afc = gAudioFlingerClient;
af = gAudioFlinger;
+ // Make sure callbacks can be received by gAudioFlingerClient
+ ProcessState::self()->startThreadPool();
}
- if (afc != 0) {
- int64_t token = IPCThreadState::self()->clearCallingIdentity();
- af->registerClient(afc);
- IPCThreadState::self()->restoreCallingIdentity(token);
- }
+ const int64_t token = IPCThreadState::self()->clearCallingIdentity();
+ af->registerClient(afc);
+ IPCThreadState::self()->restoreCallingIdentity(token);
if (reportNoError) reportError(NO_ERROR);
return af;
}
@@ -2223,8 +2236,25 @@
aps->setDevicesRoleForStrategy(strategyAidl, roleAidl, devicesAidl));
}
+status_t AudioSystem::removeDevicesRoleForStrategy(product_strategy_t strategy,
+ device_role_t role,
+ const AudioDeviceTypeAddrVector& devices) {
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) {
+ return PERMISSION_DENIED;
+ }
+
+ int32_t strategyAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_product_strategy_t_int32_t(strategy));
+ media::DeviceRole roleAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_device_role_t_DeviceRole(role));
+ std::vector<AudioDevice> devicesAidl = VALUE_OR_RETURN_STATUS(
+ convertContainer<std::vector<AudioDevice>>(devices,
+ legacy2aidl_AudioDeviceTypeAddress));
+ return statusTFromBinderStatus(
+ aps->removeDevicesRoleForStrategy(strategyAidl, roleAidl, devicesAidl));
+}
+
status_t
-AudioSystem::removeDevicesRoleForStrategy(product_strategy_t strategy, device_role_t role) {
+AudioSystem::clearDevicesRoleForStrategy(product_strategy_t strategy, device_role_t role) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) {
return PERMISSION_DENIED;
@@ -2232,7 +2262,7 @@
int32_t strategyAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_product_strategy_t_int32_t(strategy));
media::DeviceRole roleAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_device_role_t_DeviceRole(role));
return statusTFromBinderStatus(
- aps->removeDevicesRoleForStrategy(strategyAidl, roleAidl));
+ aps->clearDevicesRoleForStrategy(strategyAidl, roleAidl));
}
status_t AudioSystem::getDevicesForRoleAndStrategy(product_strategy_t strategy,
diff --git a/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl b/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
index ec5097a..fa6c733 100644
--- a/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
+++ b/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
@@ -313,6 +313,15 @@
boolean isUltrasoundSupported();
+ /**
+ * Queries if there is hardware support for requesting audio capture content from
+ * the DSP hotword pipeline.
+ *
+ * @param lookbackAudio true if additionally querying for the ability to capture audio
+ * from the pipeline prior to capture stream open.
+ */
+ boolean isHotwordStreamSupported(boolean lookbackAudio);
+
AudioProductStrategy[] listAudioProductStrategies();
int /* product_strategy_t */ getProductStrategyFromAudioAttributes(
in AudioAttributesInternal aa, boolean fallbackOnDefault);
@@ -330,7 +339,10 @@
in AudioDevice[] devices);
void removeDevicesRoleForStrategy(int /* product_strategy_t */ strategy,
- DeviceRole role);
+ DeviceRole role,
+ in AudioDevice[] devices);
+
+ void clearDevicesRoleForStrategy(int /* product_strategy_t */ strategy, DeviceRole role);
AudioDevice[] getDevicesForRoleAndStrategy(int /* product_strategy_t */ strategy,
DeviceRole role);
diff --git a/media/libaudioclient/fuzzer/Android.bp b/media/libaudioclient/fuzzer/Android.bp
index dd3e3ba..b1feb60 100644
--- a/media/libaudioclient/fuzzer/Android.bp
+++ b/media/libaudioclient/fuzzer/Android.bp
@@ -56,6 +56,7 @@
"av-types-aidl-cpp",
"capture_state_listener-aidl-cpp",
"libaudioclient_aidl_conversion",
+ "libaudio_aidl_conversion_common_cpp",
"libaudioflinger",
"libaudiofoundation",
"libaudiomanager",
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index fedf511..23b0340 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -167,6 +167,10 @@
// HotwordDetectionService.
static void setAudioFlingerBinder(const sp<IBinder>& audioFlinger);
+ // Sets a local AudioFlinger interface to be used by AudioSystem.
+ // This is used by audioserver main() to avoid binder AIDL translation.
+ static status_t setLocalAudioFlinger(const sp<IAudioFlinger>& af);
+
// helper function to obtain AudioFlinger service handle
static const sp<IAudioFlinger> get_audio_flinger();
@@ -523,7 +527,11 @@
static status_t setDevicesRoleForStrategy(product_strategy_t strategy,
device_role_t role, const AudioDeviceTypeAddrVector &devices);
- static status_t removeDevicesRoleForStrategy(product_strategy_t strategy, device_role_t role);
+ static status_t removeDevicesRoleForStrategy(product_strategy_t strategy,
+ device_role_t role, const AudioDeviceTypeAddrVector &devices);
+
+ static status_t clearDevicesRoleForStrategy(product_strategy_t strategy,
+ device_role_t role);
static status_t getDevicesForRoleAndStrategy(product_strategy_t strategy,
device_role_t role, AudioDeviceTypeAddrVector &devices);
diff --git a/media/libaudioclient/tests/Android.bp b/media/libaudioclient/tests/Android.bp
index cbcef12..dcb6c25 100644
--- a/media/libaudioclient/tests/Android.bp
+++ b/media/libaudioclient/tests/Android.bp
@@ -110,6 +110,7 @@
"capture_state_listener-aidl-cpp",
"framework-permission-aidl-cpp",
"libaudioclient_aidl_conversion",
+ "libaudio_aidl_conversion_common_cpp",
"libbase",
"libbinder",
"libcgrouprc",
diff --git a/media/libaudioclient/tests/audiosystem_tests.cpp b/media/libaudioclient/tests/audiosystem_tests.cpp
index 3dd2c95..a947105 100644
--- a/media/libaudioclient/tests/audiosystem_tests.cpp
+++ b/media/libaudioclient/tests/audiosystem_tests.cpp
@@ -405,8 +405,8 @@
EXPECT_EQ(OK, AudioSystem::getDevicesForRoleAndStrategy(mediaStrategy.getId(),
DEVICE_ROLE_PREFERRED, devices));
EXPECT_EQ(devices, outputDevices);
- EXPECT_EQ(OK, AudioSystem::removeDevicesRoleForStrategy(mediaStrategy.getId(),
- DEVICE_ROLE_PREFERRED));
+ EXPECT_EQ(OK, AudioSystem::clearDevicesRoleForStrategy(mediaStrategy.getId(),
+ DEVICE_ROLE_PREFERRED));
EXPECT_EQ(NAME_NOT_FOUND, AudioSystem::getDevicesForRoleAndStrategy(
mediaStrategy.getId(), DEVICE_ROLE_PREFERRED, devices));
}
diff --git a/media/libaudiofoundation/AudioDeviceTypeAddr.cpp b/media/libaudiofoundation/AudioDeviceTypeAddr.cpp
index 4a7e956..ad39d32 100644
--- a/media/libaudiofoundation/AudioDeviceTypeAddr.cpp
+++ b/media/libaudiofoundation/AudioDeviceTypeAddr.cpp
@@ -150,6 +150,20 @@
return remainedDevices;
}
+AudioDeviceTypeAddrVector joinDeviceTypeAddrs(
+ const AudioDeviceTypeAddrVector& devices,
+ const AudioDeviceTypeAddrVector& devicesToJoin) {
+ std::set<AudioDeviceTypeAddr> devicesSet(devices.begin(), devices.end());
+ std::set<AudioDeviceTypeAddr> devicesToJoinSet(devicesToJoin.begin(), devicesToJoin.end());
+ AudioDeviceTypeAddrVector joinedDevices;
+
+ std::set_union(devicesSet.begin(), devicesSet.end(),
+ devicesToJoinSet.begin(), devicesToJoinSet.end(),
+ std::back_inserter(joinedDevices));
+
+ return joinedDevices;
+}
+
std::string dumpAudioDeviceTypeAddrVector(const AudioDeviceTypeAddrVector& deviceTypeAddrs,
bool includeSensitiveInfo) {
std::stringstream stream;
diff --git a/media/libaudiofoundation/include/media/AudioDeviceTypeAddr.h b/media/libaudiofoundation/include/media/AudioDeviceTypeAddr.h
index 11aa222..b2f2bd0 100644
--- a/media/libaudiofoundation/include/media/AudioDeviceTypeAddr.h
+++ b/media/libaudiofoundation/include/media/AudioDeviceTypeAddr.h
@@ -84,6 +84,14 @@
const AudioDeviceTypeAddrVector& devices,
const AudioDeviceTypeAddrVector& devicesToExclude);
+/**
+ * Return a collection of AudioDeviceTypeAddrs that is the union of `devices` and
+ * `devicesToJoin`
+ */
+AudioDeviceTypeAddrVector joinDeviceTypeAddrs(
+ const AudioDeviceTypeAddrVector& devices,
+ const AudioDeviceTypeAddrVector& devicesToJoin);
+
std::string dumpAudioDeviceTypeAddrVector(const AudioDeviceTypeAddrVector& deviceTypeAddrs,
bool includeSensitiveInfo=false);
diff --git a/media/libaudiohal/Android.bp b/media/libaudiohal/Android.bp
index 320c639..f47dd0b 100644
--- a/media/libaudiohal/Android.bp
+++ b/media/libaudiohal/Android.bp
@@ -43,6 +43,7 @@
header_libs: [
"libaudiohal_headers",
"libbase_headers",
+ "liberror_headers",
"libmediautils_headers",
]
}
@@ -74,8 +75,4 @@
name: "libaudiohal_headers",
export_include_dirs: ["include"],
-
- // This is needed because the stream interface includes media/MicrophoneInfo.h
- header_libs: ["av-headers"],
- export_header_lib_headers: ["av-headers"],
}
diff --git a/media/libaudiohal/FactoryHal.cpp b/media/libaudiohal/FactoryHal.cpp
index 16d591c..84ac64c 100644
--- a/media/libaudiohal/FactoryHal.cpp
+++ b/media/libaudiohal/FactoryHal.cpp
@@ -141,7 +141,7 @@
auto halType = version.getType();
if (halType == AudioHalVersionInfo::Type::AIDL) {
return hasAidlHalService(interface, version);
- } else if (version.getType() == AudioHalVersionInfo::Type::HIDL) {
+ } else if (halType == AudioHalVersionInfo::Type::HIDL) {
return hasHidlHalService(interface, version);
} else {
ALOGE("HalType not supported %s", version.toString().c_str());
@@ -164,9 +164,9 @@
auto siblingInterfaceMap = isDevice ? sEffectsHALInterfaces : sDevicesHALInterfaces;
auto ifaceVersionIt = findMostRecentVersion(interfaceMap);
auto siblingVersionIt = findMostRecentVersion(siblingInterfaceMap);
- if (ifaceVersionIt != sAudioHALVersions.end() &&
- siblingVersionIt != sAudioHALVersions.end() &&
- // same major version
+ if (ifaceVersionIt != sAudioHALVersions.end() && siblingVersionIt != sAudioHALVersions.end() &&
+ // same HAL type (HIDL/AIDL) and same major version
+ ifaceVersionIt->getType() == siblingVersionIt->getType() &&
ifaceVersionIt->getMajorVersion() == siblingVersionIt->getMajorVersion()) {
void* rawInterface;
if (createHalService(std::max(*ifaceVersionIt, *siblingVersionIt), isDevice,
diff --git a/media/libaudiohal/impl/Android.bp b/media/libaudiohal/impl/Android.bp
index bb5601f..d151817 100644
--- a/media/libaudiohal/impl/Android.bp
+++ b/media/libaudiohal/impl/Android.bp
@@ -241,15 +241,34 @@
"libaudiohal_default",
"latest_android_hardware_audio_common_ndk_shared",
"latest_android_hardware_audio_core_ndk_shared",
- "latest_android_hardware_audio_effect_ndk_shared",
+ "latest_android_hardware_audio_effect_ndk_static",
+ "latest_android_media_audio_common_types_ndk_shared",
],
srcs: [
"DevicesFactoryHalEntry.cpp",
"DevicesFactoryHalAidl.cpp",
+ "EffectBufferHalAidl.cpp",
+ "EffectHalAidl.cpp",
"EffectsFactoryHalAidl.cpp",
"EffectsFactoryHalEntry.cpp",
],
+ static_libs: [
+ "android.hardware.common-V2-ndk",
+ "android.hardware.common.fmq-V1-ndk",
+ ],
shared_libs: [
"libbinder_ndk",
- ]
+ "libaudio_aidl_conversion_common_ndk",
+ ],
+ header_libs: [
+ "libaudio_aidl_conversion_common_util_ndk",
+ "libaudio_system_headers",
+ ],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wthread-safety",
+ "-DBACKEND_NDK",
+ ],
}
\ No newline at end of file
diff --git a/media/libaudiohal/impl/DeviceHalAidl.cpp b/media/libaudiohal/impl/DeviceHalAidl.cpp
new file mode 100644
index 0000000..d85d960
--- /dev/null
+++ b/media/libaudiohal/impl/DeviceHalAidl.cpp
@@ -0,0 +1,187 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "DeviceHalAidl"
+
+#include "DeviceHalAidl.h"
+
+status_t DeviceHalAidl::getSupportedDevices(uint32_t* devices) {
+ ALOGE("%s not implemented yet devices %p", __func__, devices);
+ return OK;
+}
+
+status_t DeviceHalAidl::initCheck() {
+ ALOGE("%s not implemented yet", __func__);
+ return OK;
+}
+
+status_t DeviceHalAidl::setVoiceVolume(float volume) {
+ mVoiceVolume = volume;
+ ALOGE("%s not implemented yet %f", __func__, volume);
+ return OK;
+}
+
+status_t DeviceHalAidl::setMasterVolume(float volume) {
+ mMasterVolume = volume;
+ ALOGE("%s not implemented yet %f", __func__, volume);
+ return OK;
+}
+
+status_t DeviceHalAidl::getMasterVolume(float *volume) {
+ *volume = mMasterVolume;
+ ALOGE("%s not implemented yet %f", __func__, *volume);
+ return OK;
+}
+
+status_t DeviceHalAidl::setMode(audio_mode_t mode) {
+ ALOGE("%s not implemented yet %u", __func__, mode);
+ return OK;
+}
+
+status_t DeviceHalAidl::setMicMute(bool state) {
+ mMicMute = state;
+ ALOGE("%s not implemented yet %d", __func__, state);
+ return OK;
+}
+status_t DeviceHalAidl::getMicMute(bool *state) {
+ *state = mMicMute;
+ ALOGE("%s not implemented yet %d", __func__, *state);
+ return OK;
+}
+status_t DeviceHalAidl::setMasterMute(bool state) {
+ mMasterMute = state;
+ ALOGE("%s not implemented yet %d", __func__, state);
+ return OK;
+}
+status_t DeviceHalAidl::getMasterMute(bool *state) {
+ *state = mMasterMute;
+ ALOGE("%s not implemented yet %d", __func__, *state);
+ return OK;
+}
+
+status_t DeviceHalAidl::setParameters(const String8& kvPairs) {
+ ALOGE("%s not implemented yet %s", __func__, kvPairs.c_str());
+ return OK;
+}
+
+status_t DeviceHalAidl::getParameters(const String8& keys, String8 *values) {
+ ALOGE("%s not implemented yet %s %s", __func__, keys.c_str(), values->c_str());
+ return OK;
+}
+
+status_t DeviceHalAidl::getInputBufferSize(const struct audio_config* config, size_t* size) {
+ ALOGE("%s not implemented yet %p %zu", __func__, config, *size);
+ return OK;
+}
+
+status_t DeviceHalAidl::openOutputStream(audio_io_handle_t handle, audio_devices_t devices,
+ audio_output_flags_t flags, struct audio_config* config,
+ const char* address,
+ sp<StreamOutHalInterface>* outStream) {
+ ALOGE("%s not implemented yet %d %u %u %p %s %p", __func__, handle, devices, flags, config,
+ address, outStream);
+ return OK;
+}
+
+status_t DeviceHalAidl::openInputStream(audio_io_handle_t handle, audio_devices_t devices,
+ struct audio_config* config, audio_input_flags_t flags,
+ const char* address, audio_source_t source,
+ audio_devices_t outputDevice,
+ const char* outputDeviceAddress,
+ sp<StreamInHalInterface>* inStream) {
+ ALOGE("%s not implemented yet %d %u %u %u %p %s %s %p %d", __func__, handle, devices,
+ outputDevice, flags, config, address, outputDeviceAddress, inStream, source);
+ return OK;
+}
+
+status_t DeviceHalAidl::supportsAudioPatches(bool* supportsPatches) {
+ *supportsPatches = true;
+ return OK;
+}
+
+status_t DeviceHalAidl::createAudioPatch(unsigned int num_sources,
+ const struct audio_port_config* sources,
+ unsigned int num_sinks,
+ const struct audio_port_config* sinks,
+ audio_patch_handle_t* patch) {
+ ALOGE("%s not implemented yet %d %p %d %p %p", __func__, num_sources, sources, num_sinks,
+ sinks, patch);
+ return OK;
+}
+
+status_t DeviceHalAidl::releaseAudioPatch(audio_patch_handle_t patch) {
+ ALOGE("%s not implemented yet patch %d", __func__, patch);
+ return OK;
+}
+
+status_t DeviceHalAidl::setAudioPortConfig(const struct audio_port_config* config) {
+ ALOGE("%s not implemented yet config %p", __func__, config);
+ return OK;
+}
+
+status_t DeviceHalAidl::getMicrophones(
+ std::vector<audio_microphone_characteristic_t>* microphones) {
+ ALOGE("%s not implemented yet microphones %p", __func__, microphones);
+ return OK;
+}
+
+status_t DeviceHalAidl::addDeviceEffect(audio_port_handle_t device, sp<EffectHalInterface> effect) {
+ if (!effect) {
+ return BAD_VALUE;
+ }
+ ALOGE("%s not implemented yet device %d", __func__, device);
+ return OK;
+}
+status_t DeviceHalAidl::removeDeviceEffect(audio_port_handle_t device,
+ sp<EffectHalInterface> effect) {
+ if (!effect) {
+ return BAD_VALUE;
+ }
+ ALOGE("%s not implemented yet device %d", __func__, device);
+ return OK;
+}
+
+status_t DeviceHalAidl::getMmapPolicyInfos(
+ media::audio::common::AudioMMapPolicyType policyType __unused,
+ std::vector<media::audio::common::AudioMMapPolicyInfo>* policyInfos __unused) {
+ ALOGE("%s not implemented yet", __func__);
+ return OK;
+}
+
+int32_t DeviceHalAidl::getAAudioMixerBurstCount() {
+ ALOGE("%s not implemented yet", __func__);
+ return OK;
+}
+
+int32_t DeviceHalAidl::getAAudioHardwareBurstMinUsec() {
+ ALOGE("%s not implemented yet", __func__);
+ return OK;
+}
+
+error::Result<audio_hw_sync_t> DeviceHalAidl::getHwAvSync() {
+ ALOGE("%s not implemented yet", __func__);
+ return base::unexpected(INVALID_OPERATION);
+}
+
+status_t DeviceHalAidl::dump(int __unused, const Vector<String16>& __unused) {
+ ALOGE("%s not implemented yet", __func__);
+ return OK;
+};
+
+int32_t DeviceHalAidl::supportsBluetoothVariableLatency(bool* supports __unused) override {
+ ALOGE("%s not implemented yet", __func__);
+ return INVALID_OPERATION;
+}
diff --git a/media/libaudiohal/impl/DeviceHalAidl.h b/media/libaudiohal/impl/DeviceHalAidl.h
new file mode 100644
index 0000000..5e8a8dd
--- /dev/null
+++ b/media/libaudiohal/impl/DeviceHalAidl.h
@@ -0,0 +1,130 @@
+/*
+ * 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 <media/audiohal/DeviceHalInterface.h>
+#include <media/audiohal/EffectHalInterface.h>
+
+#include <aidl/android/hardware/audio/core/BpModule.h>
+
+namespace android {
+
+class DeviceHalAidl : public DeviceHalInterface {
+ public:
+ // Sets the value of 'devices' to a bitmask of 1 or more values of audio_devices_t.
+ status_t getSupportedDevices(uint32_t *devices) override;
+
+ // Check to see if the audio hardware interface has been initialized.
+ status_t initCheck() override;
+
+ // Set the audio volume of a voice call. Range is between 0.0 and 1.0.
+ status_t setVoiceVolume(float volume) override;
+
+ // Set the audio volume for all audio activities other than voice call.
+ status_t setMasterVolume(float volume) override;
+
+ // Get the current master volume value for the HAL.
+ status_t getMasterVolume(float *volume) override;
+
+ // Called when the audio mode changes.
+ status_t setMode(audio_mode_t mode) override;
+
+ // Muting control.
+ status_t setMicMute(bool state) override;
+
+ status_t getMicMute(bool* state) override;
+
+ status_t setMasterMute(bool state) override;
+
+ status_t getMasterMute(bool *state) override;
+
+ // Set global audio parameters.
+ status_t setParameters(const String8& kvPairs) override;
+
+ // Get global audio parameters.
+ status_t getParameters(const String8& keys, String8 *values) override;
+
+ // Returns audio input buffer size according to parameters passed.
+ status_t getInputBufferSize(const struct audio_config* config, size_t* size) override;
+
+ // Creates and opens the audio hardware output stream. The stream is closed
+ // by releasing all references to the returned object.
+ status_t openOutputStream(audio_io_handle_t handle, audio_devices_t devices,
+ audio_output_flags_t flags, struct audio_config* config,
+ const char* address, sp<StreamOutHalInterface>* outStream) override;
+
+ // Creates and opens the audio hardware input stream. The stream is closed
+ // by releasing all references to the returned object.
+ status_t openInputStream(audio_io_handle_t handle, audio_devices_t devices,
+ struct audio_config* config, audio_input_flags_t flags,
+ const char* address, audio_source_t source,
+ audio_devices_t outputDevice, const char* outputDeviceAddress,
+ sp<StreamInHalInterface>* inStream) override;
+
+ // Returns whether createAudioPatch and releaseAudioPatch operations are supported.
+ status_t supportsAudioPatches(bool* supportsPatches) override;
+
+ // Creates an audio patch between several source and sink ports.
+ status_t createAudioPatch(unsigned int num_sources, const struct audio_port_config* sources,
+ unsigned int num_sinks, const struct audio_port_config* sinks,
+ audio_patch_handle_t* patch) override;
+
+ // Releases an audio patch.
+ status_t releaseAudioPatch(audio_patch_handle_t patch) override;
+
+ // Set audio port configuration.
+ status_t setAudioPortConfig(const struct audio_port_config* config) override;
+
+ // List microphones
+ status_t getMicrophones(std::vector<audio_microphone_characteristic_t>* microphones);
+
+ status_t addDeviceEffect(audio_port_handle_t device, sp<EffectHalInterface> effect) override;
+
+ status_t removeDeviceEffect(audio_port_handle_t device, sp<EffectHalInterface> effect) override;
+
+ status_t getMmapPolicyInfos(media::audio::common::AudioMMapPolicyType policyType __unused,
+ std::vector<media::audio::common::AudioMMapPolicyInfo>* policyInfos
+ __unused) override;
+
+ int32_t getAAudioMixerBurstCount() override;
+
+ int32_t getAAudioHardwareBurstMinUsec() override;
+
+ error::Result<audio_hw_sync_t> getHwAvSync() override;
+
+ status_t dump(int __unused, const Vector<String16>& __unused) override;
+
+ int32_t supportsBluetoothVariableLatency(bool* supports __unused) override;
+
+ private:
+ friend class DevicesFactoryHalAidl;
+ const std::shared_ptr<::aidl::android::hardware::audio::core::IModule> mCore;
+ float mMasterVolume = 0.0f;
+ float mVoiceVolume = 0.0f;
+ bool mMasterMute = false;
+ bool mMicMute = false;
+
+ // Can not be constructed directly by clients.
+ explicit DeviceHalAidl(
+ const std::shared_ptr<::aidl::android::hardware::audio::core::IModule>& core)
+ : mCore(core) {}
+
+ // The destructor automatically closes the device.
+ ~DeviceHalAidl();
+};
+
+} // namespace android
diff --git a/media/libaudiohal/impl/DeviceHalHidl.cpp b/media/libaudiohal/impl/DeviceHalHidl.cpp
index 0cdf621..be063ab 100644
--- a/media/libaudiohal/impl/DeviceHalHidl.cpp
+++ b/media/libaudiohal/impl/DeviceHalHidl.cpp
@@ -459,12 +459,13 @@
#if MAJOR_VERSION == 2
status_t DeviceHalHidl::getMicrophones(
- std::vector<media::MicrophoneInfo> *microphonesInfo __unused) {
+ std::vector<audio_microphone_characteristic_t> *microphonesInfo __unused) {
if (mDevice == 0) return NO_INIT;
return INVALID_OPERATION;
}
#elif MAJOR_VERSION >= 4
-status_t DeviceHalHidl::getMicrophones(std::vector<media::MicrophoneInfo> *microphonesInfo) {
+status_t DeviceHalHidl::getMicrophones(
+ std::vector<audio_microphone_characteristic_t> *microphonesInfo) {
TIME_CHECK();
if (mDevice == 0) return NO_INIT;
Result retval;
@@ -475,8 +476,7 @@
audio_microphone_characteristic_t dst;
//convert
(void)CoreUtils::microphoneInfoToHal(micArrayHal[k], &dst);
- media::MicrophoneInfo microphone = media::MicrophoneInfo(dst);
- microphonesInfo->push_back(microphone);
+ microphonesInfo->push_back(dst);
}
});
return processReturn("getMicrophones", ret, retval);
diff --git a/media/libaudiohal/impl/DeviceHalHidl.h b/media/libaudiohal/impl/DeviceHalHidl.h
index 3e33609..052eb65 100644
--- a/media/libaudiohal/impl/DeviceHalHidl.h
+++ b/media/libaudiohal/impl/DeviceHalHidl.h
@@ -30,87 +30,74 @@
{
public:
// Sets the value of 'devices' to a bitmask of 1 or more values of audio_devices_t.
- virtual status_t getSupportedDevices(uint32_t *devices);
+ status_t getSupportedDevices(uint32_t *devices) override;
// Check to see if the audio hardware interface has been initialized.
- virtual status_t initCheck();
+ status_t initCheck() override;
// Set the audio volume of a voice call. Range is between 0.0 and 1.0.
- virtual status_t setVoiceVolume(float volume);
+ status_t setVoiceVolume(float volume) override;
// Set the audio volume for all audio activities other than voice call.
- virtual status_t setMasterVolume(float volume);
+ status_t setMasterVolume(float volume) override;
// Get the current master volume value for the HAL.
- virtual status_t getMasterVolume(float *volume);
+ status_t getMasterVolume(float *volume) override;
// Called when the audio mode changes.
- virtual status_t setMode(audio_mode_t mode);
+ status_t setMode(audio_mode_t mode) override;
// Muting control.
- virtual status_t setMicMute(bool state);
- virtual status_t getMicMute(bool *state);
- virtual status_t setMasterMute(bool state);
- virtual status_t getMasterMute(bool *state);
+ status_t setMicMute(bool state) override;
+ status_t getMicMute(bool *state) override;
+ status_t setMasterMute(bool state) override;
+ status_t getMasterMute(bool *state) override;
// Set global audio parameters.
- virtual status_t setParameters(const String8& kvPairs);
+ status_t setParameters(const String8& kvPairs) override;
// Get global audio parameters.
- virtual status_t getParameters(const String8& keys, String8 *values);
+ status_t getParameters(const String8& keys, String8 *values) override;
// Returns audio input buffer size according to parameters passed.
- virtual status_t getInputBufferSize(const struct audio_config *config,
- size_t *size);
+ status_t getInputBufferSize(const struct audio_config* config, size_t* size) override;
// Creates and opens the audio hardware output stream. The stream is closed
// by releasing all references to the returned object.
- virtual status_t openOutputStream(
- audio_io_handle_t handle,
- audio_devices_t devices,
- audio_output_flags_t flags,
- struct audio_config *config,
- const char *address,
- sp<StreamOutHalInterface> *outStream);
+ status_t openOutputStream(audio_io_handle_t handle, audio_devices_t devices,
+ audio_output_flags_t flags, struct audio_config* config,
+ const char* address, sp<StreamOutHalInterface>* outStream) override;
// Creates and opens the audio hardware input stream. The stream is closed
// by releasing all references to the returned object.
- virtual status_t openInputStream(
- audio_io_handle_t handle,
- audio_devices_t devices,
- struct audio_config *config,
- audio_input_flags_t flags,
- const char *address,
- audio_source_t source,
- audio_devices_t outputDevice,
- const char *outputDeviceAddress,
- sp<StreamInHalInterface> *inStream);
+ status_t openInputStream(audio_io_handle_t handle, audio_devices_t devices,
+ struct audio_config* config, audio_input_flags_t flags,
+ const char* address, audio_source_t source,
+ audio_devices_t outputDevice, const char* outputDeviceAddress,
+ sp<StreamInHalInterface>* inStream) override;
// Returns whether createAudioPatch and releaseAudioPatch operations are supported.
- virtual status_t supportsAudioPatches(bool *supportsPatches);
+ status_t supportsAudioPatches(bool* supportsPatches) override;
// Creates an audio patch between several source and sink ports.
- virtual status_t createAudioPatch(
- unsigned int num_sources,
- const struct audio_port_config *sources,
- unsigned int num_sinks,
- const struct audio_port_config *sinks,
- audio_patch_handle_t *patch);
+ status_t createAudioPatch(unsigned int num_sources, const struct audio_port_config* sources,
+ unsigned int num_sinks, const struct audio_port_config* sinks,
+ audio_patch_handle_t* patch) override;
// Releases an audio patch.
- virtual status_t releaseAudioPatch(audio_patch_handle_t patch);
+ status_t releaseAudioPatch(audio_patch_handle_t patch) override;
// Fills the list of supported attributes for a given audio port.
- virtual status_t getAudioPort(struct audio_port *port);
+ status_t getAudioPort(struct audio_port *port) override;
// Fills the list of supported attributes for a given audio port.
- virtual status_t getAudioPort(struct audio_port_v7 *port);
+ status_t getAudioPort(struct audio_port_v7 *port) override;
// Set audio port configuration.
- virtual status_t setAudioPortConfig(const struct audio_port_config *config);
+ status_t setAudioPortConfig(const struct audio_port_config *config) override;
// List microphones
- virtual status_t getMicrophones(std::vector<media::MicrophoneInfo> *microphones);
+ status_t getMicrophones(std::vector<audio_microphone_characteristic_t>* microphones) override;
status_t addDeviceEffect(audio_port_handle_t device, sp<EffectHalInterface> effect) override;
status_t removeDeviceEffect(audio_port_handle_t device, sp<EffectHalInterface> effect) override;
diff --git a/media/libaudiohal/impl/DevicesFactoryHalAidl.cpp b/media/libaudiohal/impl/DevicesFactoryHalAidl.cpp
index 29fb558..b9ca164 100644
--- a/media/libaudiohal/impl/DevicesFactoryHalAidl.cpp
+++ b/media/libaudiohal/impl/DevicesFactoryHalAidl.cpp
@@ -17,18 +17,22 @@
#define LOG_TAG "DevicesFactoryHalAidl"
//#define LOG_NDEBUG 0
+#include <aidl/android/hardware/audio/core/IModule.h>
#include <android/binder_manager.h>
+#include <memory>
#include <utils/Log.h>
+#include "DeviceHalAidl.h"
#include "DevicesFactoryHalAidl.h"
+using namespace ::aidl::android::hardware::audio::core;
using ::android::detail::AudioHalVersionInfo;
namespace android {
-DevicesFactoryHalAidl::DevicesFactoryHalAidl(std::shared_ptr<IConfig> iconfig) {
+DevicesFactoryHalAidl::DevicesFactoryHalAidl(std::shared_ptr<IConfig> iconfig)
+ : mIConfig(std::move(iconfig)) {
ALOG_ASSERT(iconfig != nullptr, "Provided default IConfig service is NULL");
- mIConfig = std::move(iconfig);
}
void DevicesFactoryHalAidl::onFirstRef() {
@@ -41,8 +45,18 @@
if (name == nullptr || device == nullptr) {
return BAD_VALUE;
}
- ALOGE("%s not implemented yet", __func__);
+ ALOGE("%s not implemented yet %s", __func__, name);
return INVALID_OPERATION;
+
+ // TODO: only support primary now ("default" means "primary")
+ if (strcmp(name, "primary") != 0) {
+ auto serviceName = std::string() + IModule::descriptor + "/default";
+ auto service = IModule::fromBinder(
+ ndk::SpAIBinder(AServiceManager_waitForService(serviceName.c_str())));
+ ALOGW("%s fromBinder %s %s", __func__, IModule::descriptor, service ? "succ" : "fail");
+ *device = new DeviceHalAidl(service);
+ }
+ return OK;
}
status_t DevicesFactoryHalAidl::getHalPids(std::vector<pid_t> *pids) {
@@ -76,9 +90,14 @@
// Main entry-point to the shared library.
extern "C" __attribute__((visibility("default"))) void* createIDevicesFactoryImpl() {
+ auto serviceName = std::string(IConfig::descriptor) + "/default";
auto service = IConfig::fromBinder(
- ndk::SpAIBinder(AServiceManager_waitForService(IConfig::descriptor)));
- return service ? new DevicesFactoryHalAidl(service) : nullptr;
+ ndk::SpAIBinder(AServiceManager_waitForService(serviceName.c_str())));
+ if (!service) {
+ ALOGE("%s binder service %s not exist", __func__, serviceName.c_str());
+ return nullptr;
+ }
+ return new DevicesFactoryHalAidl(service);
}
} // namespace android
diff --git a/media/libaudiohal/impl/DevicesFactoryHalAidl.h b/media/libaudiohal/impl/DevicesFactoryHalAidl.h
index f4812af..71138a0 100644
--- a/media/libaudiohal/impl/DevicesFactoryHalAidl.h
+++ b/media/libaudiohal/impl/DevicesFactoryHalAidl.h
@@ -20,14 +20,13 @@
#include <media/audiohal/DevicesFactoryHalInterface.h>
#include <utils/RefBase.h>
-using namespace ::aidl::android::hardware::audio::core;
-
namespace android {
class DevicesFactoryHalAidl : public DevicesFactoryHalInterface
{
public:
- explicit DevicesFactoryHalAidl(std::shared_ptr<IConfig> iConfig);
+ explicit DevicesFactoryHalAidl(
+ std::shared_ptr<::aidl::android::hardware::audio::core::IConfig> iConfig);
void onFirstRef() override;
// Opens a device with the specified name. To close the device, it is
@@ -41,8 +40,8 @@
android::detail::AudioHalVersionInfo getHalVersion() const override;
private:
- std::shared_ptr<IConfig> mIConfig;
- virtual ~DevicesFactoryHalAidl() = default;
+ const std::shared_ptr<::aidl::android::hardware::audio::core::IConfig> mIConfig;
+ ~DevicesFactoryHalAidl() = default;
};
} // namespace android
diff --git a/media/libaudiohal/impl/EffectBufferHalAidl.cpp b/media/libaudiohal/impl/EffectBufferHalAidl.cpp
new file mode 100644
index 0000000..5af8e24
--- /dev/null
+++ b/media/libaudiohal/impl/EffectBufferHalAidl.cpp
@@ -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.
+ */
+
+#define LOG_TAG "EffectBufferHalAidl"
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+
+#include "EffectBufferHalAidl.h"
+
+namespace android {
+namespace effect {
+
+// static
+status_t EffectBufferHalAidl::allocate(size_t size, sp<EffectBufferHalInterface>* buffer) {
+ ALOGE("%s not implemented yet %zu %p", __func__, size, buffer);
+ return mirror(nullptr, size, buffer);
+}
+
+status_t EffectBufferHalAidl::mirror(void* external, size_t size,
+ sp<EffectBufferHalInterface>* buffer) {
+ // buffer->setExternalData(external);
+ ALOGW("%s not implemented yet %p %zu %p", __func__, external, size, buffer);
+ return OK;
+}
+
+EffectBufferHalAidl::EffectBufferHalAidl(size_t size)
+ : mBufferSize(size),
+ mFrameCountChanged(false),
+ mExternalData(nullptr),
+ mAudioBuffer{0, {nullptr}} {
+}
+
+EffectBufferHalAidl::~EffectBufferHalAidl() {
+}
+
+status_t EffectBufferHalAidl::init() {
+ ALOGW("%s not implemented yet", __func__);
+ return OK;
+}
+
+audio_buffer_t* EffectBufferHalAidl::audioBuffer() {
+ return &mAudioBuffer;
+}
+
+void* EffectBufferHalAidl::externalData() const {
+ return mExternalData;
+}
+
+void EffectBufferHalAidl::setFrameCount(size_t frameCount) {
+ mAudioBuffer.frameCount = frameCount;
+ mFrameCountChanged = true;
+}
+
+bool EffectBufferHalAidl::checkFrameCountChange() {
+ bool result = mFrameCountChanged;
+ mFrameCountChanged = false;
+ return result;
+}
+
+void EffectBufferHalAidl::setExternalData(void* external) {
+ mExternalData = external;
+}
+
+void EffectBufferHalAidl::update() {
+ ALOGW("%s not implemented yet", __func__);
+}
+
+void EffectBufferHalAidl::commit() {
+ ALOGW("%s not implemented yet", __func__);
+}
+
+} // namespace effect
+} // namespace android
diff --git a/media/libaudiohal/impl/EffectBufferHalAidl.h b/media/libaudiohal/impl/EffectBufferHalAidl.h
new file mode 100644
index 0000000..f488708
--- /dev/null
+++ b/media/libaudiohal/impl/EffectBufferHalAidl.h
@@ -0,0 +1,61 @@
+/*
+ * 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 <media/audiohal/EffectBufferHalInterface.h>
+#include <system/audio_effect.h>
+
+namespace android {
+namespace effect {
+
+class EffectBufferHalAidl : public EffectBufferHalInterface {
+ public:
+ static status_t allocate(size_t size, sp<EffectBufferHalInterface>* buffer);
+ static status_t mirror(void* external, size_t size, sp<EffectBufferHalInterface>* buffer);
+
+ audio_buffer_t* audioBuffer() override;
+ void* externalData() const override;
+
+ size_t getSize() const override { return mBufferSize; }
+
+ void setExternalData(void* external) override;
+ void setFrameCount(size_t frameCount) override;
+ bool checkFrameCountChange() override;
+
+ void update() override;
+ void commit() override;
+ void update(size_t size) override;
+ void commit(size_t size) override;
+
+ private:
+ friend class EffectBufferHalInterface;
+
+ const size_t mBufferSize;
+ bool mFrameCountChanged;
+ void* mExternalData;
+ audio_buffer_t mAudioBuffer;
+
+ // Can not be constructed directly by clients.
+ explicit EffectBufferHalAidl(size_t size);
+
+ ~EffectBufferHalAidl();
+
+ status_t init();
+};
+
+} // namespace effect
+} // namespace android
diff --git a/media/libaudiohal/impl/EffectHalAidl.cpp b/media/libaudiohal/impl/EffectHalAidl.cpp
new file mode 100644
index 0000000..31c5ca5
--- /dev/null
+++ b/media/libaudiohal/impl/EffectHalAidl.cpp
@@ -0,0 +1,234 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "EffectHalAidl"
+//#define LOG_NDEBUG 0
+
+#include <media/AidlConversionCppNdk.h>
+#include <media/AidlConversionNdk.h>
+#include <media/audiohal/AudioHalUtils.h>
+#include <media/EffectsFactoryApi.h>
+#include <mediautils/TimeCheck.h>
+#include <utils/Log.h>
+
+#include "EffectHalAidl.h"
+
+#include <system/audio.h>
+
+#include <aidl/android/hardware/audio/effect/IEffect.h>
+
+using ::aidl::android::hardware::audio::effect::CommandId;
+using ::aidl::android::hardware::audio::effect::Descriptor;
+using ::aidl::android::hardware::audio::effect::IEffect;
+using ::aidl::android::hardware::audio::effect::State;
+using ::aidl::android::hardware::audio::effect::Parameter;
+
+namespace android {
+namespace effect {
+
+EffectHalAidl::EffectHalAidl(const std::shared_ptr<IEffect>& effect, uint64_t effectId,
+ int32_t sessionId, int32_t ioId)
+ : mEffectId(effectId), mSessionId(sessionId), mIoId(ioId), mEffect(effect) {}
+
+EffectHalAidl::~EffectHalAidl() {}
+
+status_t EffectHalAidl::setInBuffer(const sp<EffectBufferHalInterface>& buffer) {
+ if (buffer == nullptr) {
+ return BAD_VALUE;
+ }
+ ALOGW("%s not implemented yet", __func__);
+ return OK;
+}
+
+status_t EffectHalAidl::setOutBuffer(const sp<EffectBufferHalInterface>& buffer) {
+ if (buffer == nullptr) {
+ return BAD_VALUE;
+ }
+ ALOGW("%s not implemented yet", __func__);
+ return OK;
+}
+
+status_t EffectHalAidl::process() {
+ ALOGW("%s not implemented yet", __func__);
+ // write to input FMQ here?
+ return OK;
+}
+
+// TODO: no one using, maybe deprecate this interface
+status_t EffectHalAidl::processReverse() {
+ ALOGW("%s not implemented yet", __func__);
+ return OK;
+}
+
+status_t EffectHalAidl::handleSetConfig(uint32_t cmdCode, uint32_t cmdSize, void* pCmdData,
+ uint32_t* replySize, void* pReplyData) {
+ if (pCmdData == NULL || cmdSize != sizeof(effect_config_t) || replySize == NULL ||
+ *replySize != sizeof(int32_t) || pReplyData == NULL) {
+ ALOGE("%s parameter error code %u", __func__, cmdCode);
+ return BAD_VALUE;
+ }
+
+ *static_cast<int32_t*>(pReplyData) = FAILED_TRANSACTION;
+ memcpy(&mConfig, pCmdData, cmdSize);
+
+ State state;
+ RETURN_IF_BINDER_FAIL(mEffect->getState(&state));
+ // effect not open yet, save settings locally
+ if (state != State::INIT) {
+ effect_config_t* legacyConfig = static_cast<effect_config_t*>(pCmdData);
+ // already open, apply latest settings
+ Parameter aidlParam;
+ Parameter::Common aidlCommon;
+ aidlCommon.input.base =
+ VALUE_OR_RETURN_STATUS(::aidl::android::legacy2aidl_AudioConfigBase_buffer_config_t(
+ legacyConfig->inputCfg, true /* isInput */));
+ aidlCommon.output.base =
+ VALUE_OR_RETURN_STATUS(::aidl::android::legacy2aidl_AudioConfigBase_buffer_config_t(
+ legacyConfig->outputCfg, false /* isInput */));
+ aidlCommon.session = mSessionId;
+ aidlCommon.ioHandle = mIoId;
+ Parameter::Id id;
+ id.set<Parameter::Id::commonTag>(Parameter::common);
+ aidlParam.set<Parameter::common>(aidlCommon);
+ RETURN_IF_BINDER_FAIL(mEffect->setParameter(aidlParam));
+ }
+ *(int*)pReplyData = 0;
+ *static_cast<int32_t*>(pReplyData) = OK;
+ return OK;
+}
+
+status_t EffectHalAidl::handleGetConfig(uint32_t cmdCode, uint32_t cmdSize, void* pCmdData,
+ uint32_t* replySize, void* pReplyData) {
+ if (pCmdData == NULL || cmdSize == 0 || replySize == NULL ||
+ *replySize != sizeof(effect_config_t) || pReplyData == NULL) {
+ ALOGE("%s parameter error with cmdCode %d", __func__, cmdCode);
+ return BAD_VALUE;
+ }
+
+ *(effect_config_t*)pReplyData = mConfig;
+ return OK;
+}
+
+status_t EffectHalAidl::handleSetParameter(uint32_t cmdCode, uint32_t cmdSize, void* pCmdData,
+ uint32_t* replySize, void* pReplyData) {
+ ALOGW("%s not implemented yet", __func__);
+ if (pCmdData == NULL || cmdSize == 0 || replySize == NULL ||
+ *replySize != sizeof(effect_config_t) || pReplyData == NULL) {
+ ALOGE("%s parameter error with cmdCode %d", __func__, cmdCode);
+ return BAD_VALUE;
+ }
+ return OK;
+}
+
+status_t EffectHalAidl::handleGetParameter(uint32_t cmdCode, uint32_t cmdSize, void* pCmdData,
+ uint32_t* replySize, void* pReplyData) {
+ ALOGW("%s not implemented yet", __func__);
+ if (pCmdData == NULL || cmdSize == 0 || replySize == NULL ||
+ *replySize != sizeof(effect_config_t) || pReplyData == NULL) {
+ ALOGE("%s parameter error with cmdCode %d", __func__, cmdCode);
+ return BAD_VALUE;
+ }
+ return OK;
+}
+
+status_t EffectHalAidl::command(uint32_t cmdCode, uint32_t cmdSize, void* pCmdData,
+ uint32_t* replySize, void* pReplyData) {
+ ALOGW("%s code %d not implemented yet", __func__, cmdCode);
+ ::ndk::ScopedAStatus status;
+ switch (cmdCode) {
+ case EFFECT_CMD_INIT: {
+ // open with default effect_config_t (convert to Parameter.Common)
+ IEffect::OpenEffectReturn ret;
+ Parameter::Common common;
+ RETURN_IF_BINDER_FAIL(mEffect->open(common, std::nullopt, &ret));
+ return OK;
+ }
+ case EFFECT_CMD_SET_CONFIG:
+ return handleSetConfig(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
+ case EFFECT_CMD_GET_CONFIG:
+ return handleGetConfig(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
+ case EFFECT_CMD_RESET:
+ return mEffect->command(CommandId::RESET).getStatus();
+ case EFFECT_CMD_ENABLE:
+ return mEffect->command(CommandId::START).getStatus();
+ case EFFECT_CMD_DISABLE:
+ return mEffect->command(CommandId::STOP).getStatus();
+ case EFFECT_CMD_SET_PARAM:
+ return handleSetParameter(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
+ case EFFECT_CMD_SET_PARAM_DEFERRED:
+ case EFFECT_CMD_SET_PARAM_COMMIT:
+ // TODO
+ return OK;
+ case EFFECT_CMD_GET_PARAM:
+ return handleGetParameter(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
+ case EFFECT_CMD_SET_DEVICE:
+ return OK;
+ case EFFECT_CMD_SET_VOLUME:
+ return OK;
+ case EFFECT_CMD_SET_AUDIO_MODE:
+ return OK;
+ case EFFECT_CMD_SET_CONFIG_REVERSE:
+ return OK;
+ case EFFECT_CMD_SET_INPUT_DEVICE:
+ return OK;
+ case EFFECT_CMD_GET_CONFIG_REVERSE:
+ return OK;
+ case EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS:
+ return OK;
+ case EFFECT_CMD_GET_FEATURE_CONFIG:
+ return OK;
+ case EFFECT_CMD_SET_FEATURE_CONFIG:
+ return OK;
+ case EFFECT_CMD_SET_AUDIO_SOURCE:
+ return OK;
+ case EFFECT_CMD_OFFLOAD:
+ return OK;
+ case EFFECT_CMD_DUMP:
+ return OK;
+ case EFFECT_CMD_FIRST_PROPRIETARY:
+ return OK;
+ default:
+ return INVALID_OPERATION;
+ }
+ return INVALID_OPERATION;
+}
+
+status_t EffectHalAidl::getDescriptor(effect_descriptor_t* pDescriptor) {
+ ALOGW("%s %p", __func__, pDescriptor);
+ if (pDescriptor == nullptr) {
+ return BAD_VALUE;
+ }
+ Descriptor aidlDesc;
+ RETURN_IF_BINDER_FAIL(mEffect->getDescriptor(&aidlDesc));
+
+ *pDescriptor = VALUE_OR_RETURN_STATUS(
+ ::aidl::android::aidl2legacy_Descriptor_effect_descriptor(aidlDesc));
+ return OK;
+}
+
+status_t EffectHalAidl::close() {
+ auto ret = mEffect->close();
+ ALOGI("%s %s", __func__, ret.getMessage());
+ return ret.getStatus();
+}
+
+status_t EffectHalAidl::dump(int fd) {
+ ALOGW("%s not implemented yet, fd %d", __func__, fd);
+ return OK;
+}
+
+} // namespace effect
+} // namespace android
diff --git a/media/libaudiohal/impl/EffectHalAidl.h b/media/libaudiohal/impl/EffectHalAidl.h
new file mode 100644
index 0000000..76bb240
--- /dev/null
+++ b/media/libaudiohal/impl/EffectHalAidl.h
@@ -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 <aidl/android/hardware/audio/effect/IEffect.h>
+#include <media/audiohal/EffectHalInterface.h>
+#include <system/audio_effect.h>
+
+namespace android {
+namespace effect {
+
+class EffectHalAidl : public EffectHalInterface {
+ public:
+ // Set the input buffer.
+ status_t setInBuffer(const sp<EffectBufferHalInterface>& buffer) override;
+
+ // Set the output buffer.
+ status_t setOutBuffer(const sp<EffectBufferHalInterface>& buffer) override;
+
+ // Effect process function.
+ status_t process() override;
+
+ // Process reverse stream function. This function is used to pass
+ // a reference stream to the effect engine.
+ status_t processReverse() override;
+
+ // Send a command and receive a response to/from effect engine.
+ status_t command(uint32_t cmdCode, uint32_t cmdSize, void* pCmdData, uint32_t* replySize,
+ void* pReplyData) override;
+
+ // Returns the effect descriptor.
+ status_t getDescriptor(effect_descriptor_t *pDescriptor) override;
+
+ // Free resources on the remote side.
+ status_t close() override;
+
+ // Whether it's a local implementation.
+ bool isLocal() const override { return false; }
+
+ status_t dump(int fd) override;
+
+ uint64_t effectId() const override { return mEffectId; }
+
+ private:
+ friend class EffectsFactoryHalAidl;
+
+ const uint64_t mEffectId;
+ const int32_t mSessionId;
+ const int32_t mIoId;
+ sp<EffectBufferHalInterface> mInBuffer, mOutBuffer;
+ effect_config_t mConfig;
+ std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect> mEffect;
+
+ // Can not be constructed directly by clients.
+ EffectHalAidl(const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& effect,
+ uint64_t effectId, int32_t sessionId, int32_t ioId);
+
+ status_t handleSetConfig(uint32_t cmdCode, uint32_t cmdSize, void* pCmdData,
+ uint32_t* replySize, void* pReplyData);
+ status_t handleGetConfig(uint32_t cmdCode, uint32_t cmdSize, void* pCmdData,
+ uint32_t* replySize, void* pReplyData);
+ status_t handleSetParameter(uint32_t cmdCode, uint32_t cmdSize, void* pCmdData,
+ uint32_t* replySize, void* pReplyData);
+ status_t handleGetParameter(uint32_t cmdCode, uint32_t cmdSize, void* pCmdData,
+ uint32_t* replySize, void* pReplyData);
+
+ // The destructor automatically releases the effect.
+ virtual ~EffectHalAidl();
+};
+
+} // namespace effect
+} // namespace android
diff --git a/media/libaudiohal/impl/EffectsFactoryHalAidl.cpp b/media/libaudiohal/impl/EffectsFactoryHalAidl.cpp
index 7aec859..0039c86 100644
--- a/media/libaudiohal/impl/EffectsFactoryHalAidl.cpp
+++ b/media/libaudiohal/impl/EffectsFactoryHalAidl.cpp
@@ -14,69 +14,122 @@
* limitations under the License.
*/
-#include <random>
+#include <algorithm>
+#include <cstdint>
+#include <memory>
#define LOG_TAG "EffectsFactoryHalAidl"
//#define LOG_NDEBUG 0
#include <aidl/android/hardware/audio/effect/IFactory.h>
#include <android/binder_manager.h>
+#include <media/AidlConversionCppNdk.h>
+#include <media/AidlConversionNdk.h>
+#include <media/audiohal/AudioHalUtils.h>
#include <utils/Log.h>
+#include "EffectBufferHalAidl.h"
+#include "EffectHalAidl.h"
#include "EffectsFactoryHalAidl.h"
using aidl::android::hardware::audio::effect::IFactory;
+using aidl::android::media::audio::common::AudioUuid;
using android::detail::AudioHalVersionInfo;
namespace android {
namespace effect {
-EffectsFactoryHalAidl::EffectsFactoryHalAidl(std::shared_ptr<IFactory> effectsFactory) {
+EffectsFactoryHalAidl::EffectsFactoryHalAidl(std::shared_ptr<IFactory> effectsFactory)
+ : mFactory(effectsFactory),
+ mHalVersion(AudioHalVersionInfo(AudioHalVersionInfo::Type::AIDL, [this]() {
+ int32_t majorVersion = 0;
+ return (mFactory && mFactory->getInterfaceVersion(&majorVersion).isOk()) ? majorVersion
+ : 0;
+ }())) {
ALOG_ASSERT(effectsFactory != nullptr, "Provided IEffectsFactory service is NULL");
- mEffectsFactory = effectsFactory;
}
status_t EffectsFactoryHalAidl::queryNumberEffects(uint32_t *pNumEffects) {
if (pNumEffects == nullptr) {
return BAD_VALUE;
}
- ALOGE("%s not implemented yet", __func__);
- return INVALID_OPERATION;
+
+ {
+ std::lock_guard lg(mLock);
+ RETURN_IF_NOT_OK(queryEffectList_l());
+ *pNumEffects = mDescList->size();
+ }
+ ALOGI("%s %d", __func__, *pNumEffects);
+ return OK;
}
status_t EffectsFactoryHalAidl::getDescriptor(uint32_t index, effect_descriptor_t* pDescriptor) {
- if (index < 0 || pDescriptor == nullptr) {
+ if (pDescriptor == nullptr) {
return BAD_VALUE;
}
- ALOGE("%s not implemented yet", __func__);
- return INVALID_OPERATION;
+
+ std::lock_guard lg(mLock);
+ RETURN_IF_NOT_OK(queryEffectList_l());
+
+ auto listSize = mDescList->size();
+ if (index >= listSize) {
+ ALOGE("%s index %d exceed size DescList %zd", __func__, index, listSize);
+ return INVALID_OPERATION;
+ }
+
+ *pDescriptor = VALUE_OR_RETURN_STATUS(
+ ::aidl::android::aidl2legacy_Descriptor_effect_descriptor(mDescList->at(index)));
+ return OK;
}
-status_t EffectsFactoryHalAidl::getDescriptor(const effect_uuid_t* pEffectUuid,
+status_t EffectsFactoryHalAidl::getDescriptor(const effect_uuid_t* halUuid,
effect_descriptor_t* pDescriptor) {
- if (pEffectUuid == nullptr || pDescriptor == nullptr) {
+ if (halUuid == nullptr || pDescriptor == nullptr) {
return BAD_VALUE;
}
- ALOGE("%s not implemented yet", __func__);
- return INVALID_OPERATION;
+
+ AudioUuid uuid =
+ VALUE_OR_RETURN_STATUS(::aidl::android::legacy2aidl_audio_uuid_t_AudioUuid(*halUuid));
+ std::lock_guard lg(mLock);
+ return getHalDescriptorWithImplUuid_l(uuid, pDescriptor);
}
-status_t EffectsFactoryHalAidl::getDescriptors(const effect_uuid_t* pEffectType,
+status_t EffectsFactoryHalAidl::getDescriptors(const effect_uuid_t* halType,
std::vector<effect_descriptor_t>* descriptors) {
- if (pEffectType == nullptr || descriptors == nullptr) {
+ if (halType == nullptr || descriptors == nullptr) {
return BAD_VALUE;
}
- ALOGE("%s not implemented yet", __func__);
- return INVALID_OPERATION;
+
+ AudioUuid type =
+ VALUE_OR_RETURN_STATUS(::aidl::android::legacy2aidl_audio_uuid_t_AudioUuid(*halType));
+ std::lock_guard lg(mLock);
+ return getHalDescriptorWithTypeUuid_l(type, descriptors);
}
-status_t EffectsFactoryHalAidl::createEffect(const effect_uuid_t* pEffectUuid, int32_t sessionId,
+status_t EffectsFactoryHalAidl::createEffect(const effect_uuid_t* uuid, int32_t sessionId,
int32_t ioId, int32_t deviceId __unused,
sp<EffectHalInterface>* effect) {
- if (pEffectUuid == nullptr || effect == nullptr) {
+ if (uuid == nullptr || effect == nullptr) {
return BAD_VALUE;
}
- ALOGE("%s not implemented yet %d %d", __func__, sessionId, ioId);
- return INVALID_OPERATION;
+ ALOGI("%s session %d ioId %d", __func__, sessionId, ioId);
+
+ AudioUuid aidlUuid =
+ VALUE_OR_RETURN_STATUS(::aidl::android::legacy2aidl_audio_uuid_t_AudioUuid(*uuid));
+ std::shared_ptr<IEffect> aidlEffect;
+ ndk::ScopedAStatus status = mFactory->createEffect(aidlUuid, &aidlEffect);
+ if (!status.isOk() || aidlEffect == nullptr) {
+ ALOGE("%s IFactory::createFactory failed %s UUID %s", __func__,
+ status.getDescription().c_str(), aidlUuid.toString().c_str());
+ return INVALID_OPERATION;
+ }
+ uint64_t effectId;
+ {
+ std::lock_guard lg(mLock);
+ effectId = ++mEffectIdCounter;
+ }
+
+ *effect = new EffectHalAidl(aidlEffect, effectId, sessionId, ioId);
+ return OK;
}
status_t EffectsFactoryHalAidl::dumpEffects(int fd) {
@@ -85,33 +138,77 @@
}
status_t EffectsFactoryHalAidl::allocateBuffer(size_t size, sp<EffectBufferHalInterface>* buffer) {
- if (size <= 0 || buffer == nullptr) {
- return BAD_VALUE;
- }
- ALOGE("%s not implemented yet", __func__);
- return INVALID_OPERATION;
+ ALOGI("%s size %zu buffer %p", __func__, size, buffer);
+ // Buffer doesn't allocated here for AIDL, instead each effect open will return I/O data FMQ.
+ return EffectBufferHalAidl::allocate(size, buffer);
}
status_t EffectsFactoryHalAidl::mirrorBuffer(void* external, size_t size,
sp<EffectBufferHalInterface>* buffer) {
- if (external == nullptr || size <= 0 || buffer == nullptr) {
- return BAD_VALUE;
- }
- ALOGE("%s not implemented yet", __func__);
- return INVALID_OPERATION;
+ ALOGI("%s extern %p size %zu buffer %p", __func__, external, size, buffer);
+ // TODO: implement with FMQ
+ return EffectBufferHalAidl::mirror(external, size, buffer);
}
AudioHalVersionInfo EffectsFactoryHalAidl::getHalVersion() const {
- int32_t versionNumber = 0;
- if (mEffectsFactory) {
- if (!mEffectsFactory->getInterfaceVersion(&versionNumber).isOk()) {
- ALOGE("%s getInterfaceVersion failed", __func__);
- } else {
- ALOGI("%s getInterfaceVersion %d", __func__, versionNumber);
+ return mHalVersion;
+}
+
+status_t EffectsFactoryHalAidl::queryEffectList_l() {
+ if (!mDescList) {
+ std::vector<Descriptor> list;
+ auto status = mFactory->queryEffects(std::nullopt, std::nullopt, std::nullopt, &list);
+ if (!status.isOk()) {
+ ALOGE("%s IFactory::queryEffects failed %s", __func__, status.getDescription().c_str());
+ return status.getStatus();
}
+
+ mDescList = std::make_unique<std::vector<Descriptor>>(list);
}
- // AIDL does not have minor version, fill 0 for all versions
- return AudioHalVersionInfo(AudioHalVersionInfo::Type::AIDL, versionNumber);
+ return OK;
+}
+
+status_t EffectsFactoryHalAidl::getHalDescriptorWithImplUuid_l(const AudioUuid& uuid,
+ effect_descriptor_t* pDescriptor) {
+ if (pDescriptor == nullptr) {
+ return BAD_VALUE;
+ }
+ if (!mDescList) {
+ RETURN_IF_NOT_OK(queryEffectList_l());
+ }
+
+ auto matchIt = std::find_if(mDescList->begin(), mDescList->end(),
+ [&](const auto& desc) { return desc.common.id.uuid == uuid; });
+ if (matchIt == mDescList->end()) {
+ ALOGE("%s UUID %s not found", __func__, uuid.toString().c_str());
+ return BAD_VALUE;
+ }
+
+ *pDescriptor = VALUE_OR_RETURN_STATUS(
+ ::aidl::android::aidl2legacy_Descriptor_effect_descriptor(*matchIt));
+ return OK;
+}
+
+status_t EffectsFactoryHalAidl::getHalDescriptorWithTypeUuid_l(
+ const AudioUuid& type, std::vector<effect_descriptor_t>* descriptors) {
+ if (descriptors == nullptr) {
+ return BAD_VALUE;
+ }
+ if (!mDescList) {
+ RETURN_IF_NOT_OK(queryEffectList_l());
+ }
+ std::vector<Descriptor> result;
+ std::copy_if(mDescList->begin(), mDescList->end(), std::back_inserter(result),
+ [&](auto& desc) { return desc.common.id.type == type; });
+ if (result.size() == 0) {
+ ALOGE("%s type UUID %s not found", __func__, type.toString().c_str());
+ return BAD_VALUE;
+ }
+
+ *descriptors = VALUE_OR_RETURN_STATUS(
+ aidl::android::convertContainer<std::vector<effect_descriptor_t>>(
+ result, ::aidl::android::aidl2legacy_Descriptor_effect_descriptor));
+ return OK;
}
} // namespace effect
@@ -120,9 +217,14 @@
// exports from a static library are optimized out unless actually used by
// the shared library. See EffectsFactoryHalEntry.cpp.
extern "C" void* createIEffectsFactoryImpl() {
- auto factory = IFactory::fromBinder(
- ndk::SpAIBinder(AServiceManager_waitForService(IFactory::descriptor)));
- return factory ? new effect::EffectsFactoryHalAidl(factory) : nullptr;
+ auto serviceName = std::string(IFactory::descriptor) + "/default";
+ auto service = IFactory::fromBinder(
+ ndk::SpAIBinder(AServiceManager_waitForService(serviceName.c_str())));
+ if (!service) {
+ ALOGE("%s binder service %s not exist", __func__, serviceName.c_str());
+ return nullptr;
+ }
+ return new effect::EffectsFactoryHalAidl(service);
}
} // namespace android
diff --git a/media/libaudiohal/impl/EffectsFactoryHalAidl.h b/media/libaudiohal/impl/EffectsFactoryHalAidl.h
index d6b5684..1e85da9 100644
--- a/media/libaudiohal/impl/EffectsFactoryHalAidl.h
+++ b/media/libaudiohal/impl/EffectsFactoryHalAidl.h
@@ -16,7 +16,13 @@
#pragma once
+#include <cstddef>
+#include <memory>
+#include <mutex>
+
+#include <android-base/thread_annotations.h>
#include <media/audiohal/EffectsFactoryHalInterface.h>
+#include <system/thread_defs.h>
namespace android {
namespace effect {
@@ -40,8 +46,8 @@
std::vector<effect_descriptor_t>* descriptors) override;
// Creates an effect engine of the specified type.
- // To release the effect engine, it is necessary to release references
- // to the returned effect object.
+ // To release the effect engine, it is necessary to release references to the returned effect
+ // object.
status_t createEffect(const effect_uuid_t* pEffectUuid, int32_t sessionId, int32_t ioId,
int32_t deviceId, sp<EffectHalInterface>* effect) override;
@@ -51,11 +57,23 @@
status_t mirrorBuffer(void* external, size_t size,
sp<EffectBufferHalInterface>* buffer) override;
- android::detail::AudioHalVersionInfo getHalVersion() const override;
+ detail::AudioHalVersionInfo getHalVersion() const override;
private:
- std::shared_ptr<IFactory> mEffectsFactory;
+ std::mutex mLock;
+ const std::shared_ptr<IFactory> mFactory;
+ uint64_t mEffectIdCounter GUARDED_BY(mLock) = 0; // Align with HIDL (0 is INVALID_ID)
+ std::unique_ptr<std::vector<Descriptor>> mDescList GUARDED_BY(mLock) = nullptr;
+ const detail::AudioHalVersionInfo mHalVersion;
+
virtual ~EffectsFactoryHalAidl() = default;
+ status_t queryEffectList_l() REQUIRES(mLock);
+ status_t getHalDescriptorWithImplUuid_l(
+ const aidl::android::media::audio::common::AudioUuid& uuid,
+ effect_descriptor_t* pDescriptor) REQUIRES(mLock);
+ status_t getHalDescriptorWithTypeUuid_l(
+ const aidl::android::media::audio::common::AudioUuid& type,
+ std::vector<effect_descriptor_t>* descriptors) REQUIRES(mLock);
};
} // namespace effect
diff --git a/media/libaudiohal/include/media/audiohal/AudioHalUtils.h b/media/libaudiohal/include/media/audiohal/AudioHalUtils.h
new file mode 100644
index 0000000..4862cba
--- /dev/null
+++ b/media/libaudiohal/include/media/audiohal/AudioHalUtils.h
@@ -0,0 +1,35 @@
+/*
+ * 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
+
+#define RETURN_IF_BINDER_FAIL(expr) \
+ do { \
+ const ::ndk::ScopedAStatus _temp_status_ = (expr); \
+ if (!_temp_status_.isOk()) { \
+ ALOGE("%s:%d return with expr %s msg %s", __func__, __LINE__, #expr, \
+ _temp_status_.getMessage()); \
+ return _temp_status_.getStatus(); \
+ } \
+ } while (false)
+
+#define RETURN_IF_NOT_OK(statement) \
+ do { \
+ auto tmp = (statement); \
+ if (tmp != OK) { \
+ return tmp; \
+ } \
+ } while (false)
diff --git a/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h b/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h
index 3858607..2c8219e 100644
--- a/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h
+++ b/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h
@@ -21,7 +21,6 @@
#include <android/media/audio/common/AudioMMapPolicyType.h>
#include <error/Result.h>
#include <media/audiohal/EffectHalInterface.h>
-#include <media/MicrophoneInfo.h>
#include <system/audio.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
@@ -107,16 +106,23 @@
virtual status_t releaseAudioPatch(audio_patch_handle_t patch) = 0;
// Fills the list of supported attributes for a given audio port.
- virtual status_t getAudioPort(struct audio_port *port) = 0;
+ virtual status_t getAudioPort(struct audio_port* port) {
+ ALOGE("%s override me port %p", __func__, port);
+ return OK;
+ }
// Fills the list of supported attributes for a given audio port.
- virtual status_t getAudioPort(struct audio_port_v7 *port) = 0;
+ virtual status_t getAudioPort(struct audio_port_v7 *port) {
+ ALOGE("%s override me port %p", __func__, port);
+ return OK;
+ }
// Set audio port configuration.
virtual status_t setAudioPortConfig(const struct audio_port_config *config) = 0;
// List microphones
- virtual status_t getMicrophones(std::vector<media::MicrophoneInfo> *microphones) = 0;
+ virtual status_t getMicrophones(
+ std::vector<audio_microphone_characteristic_t>* microphones) = 0;
virtual status_t addDeviceEffect(
audio_port_handle_t device, sp<EffectHalInterface> effect) = 0;
@@ -125,13 +131,16 @@
virtual status_t getMmapPolicyInfos(
media::audio::common::AudioMMapPolicyType policyType,
- std::vector<media::audio::common::AudioMMapPolicyInfo> *policyInfos) = 0;
+ std::vector<media::audio::common::AudioMMapPolicyInfo> *policyInfos) = 0;
virtual int32_t getAAudioMixerBurstCount() = 0;
virtual int32_t getAAudioHardwareBurstMinUsec() = 0;
virtual int32_t supportsBluetoothVariableLatency(bool* supports) = 0;
// Update the connection status of an external device.
- virtual status_t setConnectedState(const struct audio_port_v7 *port, bool connected) = 0;
+ virtual status_t setConnectedState(const struct audio_port_v7* port, bool connected) {
+ ALOGE("%s override me port %p connected %d", __func__, port, connected);
+ return OK;
+ }
virtual error::Result<audio_hw_sync_t> getHwAvSync() = 0;
diff --git a/media/libaudiohal/tests/Android.bp b/media/libaudiohal/tests/Android.bp
new file mode 100644
index 0000000..e20f74c
--- /dev/null
+++ b/media/libaudiohal/tests/Android.bp
@@ -0,0 +1,51 @@
+// 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.
+
+// Headers module is in frameworks/av/Android.bp because modules are not allowed
+// to refer to headers in parent directories and the headers live in
+// frameworks/av/include.
+
+package {
+ default_applicable_licenses: ["frameworks_av_license"],
+}
+
+cc_test {
+ name: "EffectsFactoryHalInterfaceTest",
+ test_suites: ["device-tests"],
+
+ srcs: [
+ "EffectsFactoryHalInterface_test.cpp",
+ ],
+
+ defaults: [
+ "latest_android_media_audio_common_types_cpp_shared",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wthread-safety",
+ ],
+
+ shared_libs: [
+ "audioclient-types-aidl-cpp",
+ "libaudiohal",
+ "libutils",
+ ],
+
+ header_libs: [
+ "libaudiohal_headers",
+ ],
+}
diff --git a/media/libaudiohal/tests/EffectsFactoryHalInterface_test.cpp b/media/libaudiohal/tests/EffectsFactoryHalInterface_test.cpp
new file mode 100644
index 0000000..83c7809
--- /dev/null
+++ b/media/libaudiohal/tests/EffectsFactoryHalInterface_test.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright 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.
+ */
+
+//#define LOG_NDEBUG 0
+#include <cstdint>
+#define LOG_TAG "EffectsFactoryHalInterfaceTest"
+
+#include <media/audiohal/EffectsFactoryHalInterface.h>
+
+#include <gtest/gtest.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+// EffectsFactoryHalInterface
+TEST(libAudioHalTest, createEffectsFactoryHalInterface) {
+ ASSERT_NE(nullptr, EffectsFactoryHalInterface::create());
+}
+
+TEST(libAudioHalTest, queryNumberEffects) {
+ auto factory = EffectsFactoryHalInterface::create();
+ ASSERT_NE(nullptr, factory);
+
+ uint32_t numEffects = 0;
+ EXPECT_EQ(OK, factory->queryNumberEffects(&numEffects));
+ EXPECT_NE(0ul, numEffects);
+}
+
+TEST(libAudioHalTest, getDescriptorByNumber) {
+ auto factory = EffectsFactoryHalInterface::create();
+ ASSERT_NE(nullptr, factory);
+
+ uint32_t numEffects = 0;
+ EXPECT_EQ(OK, factory->queryNumberEffects(&numEffects));
+ EXPECT_NE(0ul, numEffects);
+
+ effect_descriptor_t desc;
+ for (uint32_t i = 0; i < numEffects; i++) {
+ EXPECT_EQ(OK, factory->getDescriptor(i, &desc));
+ }
+}
+
+TEST(libAudioHalTest, createEffect) {
+ auto factory = EffectsFactoryHalInterface::create();
+ ASSERT_NE(nullptr, factory);
+
+ uint32_t numEffects = 0;
+ EXPECT_EQ(OK, factory->queryNumberEffects(&numEffects));
+ EXPECT_NE(0ul, numEffects);
+
+ effect_descriptor_t desc;
+ for (uint32_t i = 0; i < numEffects; i++) {
+ sp<EffectHalInterface> interface;
+ EXPECT_EQ(OK, factory->getDescriptor(i, &desc));
+ EXPECT_EQ(OK, factory->createEffect(&desc.uuid, 1 /* sessionId */, 1 /* ioId */,
+ 1 /* deviceId */, &interface));
+ }
+}
+
+TEST(libAudioHalTest, getHalVersion) {
+ auto factory = EffectsFactoryHalInterface::create();
+ ASSERT_NE(nullptr, factory);
+
+ auto version = factory->getHalVersion();
+ EXPECT_NE(0, version.getMajorVersion());
+}
+
+// TODO: b/263986405 Add multi-thread testing
+
+} // namespace android
diff --git a/media/libeffects/dynamicsproc/Android.bp b/media/libeffects/dynamicsproc/Android.bp
index 84131a4..f655c4f 100644
--- a/media/libeffects/dynamicsproc/Android.bp
+++ b/media/libeffects/dynamicsproc/Android.bp
@@ -32,34 +32,68 @@
],
}
+cc_defaults {
+ name : "dynamicsprocessingdefaults",
+ srcs: [
+ "dsp/DPBase.cpp",
+ "dsp/DPFrequency.cpp",
+ ],
+
+ shared_libs: [
+ "libaudioutils",
+ "libbase",
+ "liblog",
+ "libutils",
+ ],
+ header_libs: [
+ "libaudioeffects",
+ "libeigen",
+ ],
+ cflags: [
+ "-Wthread-safety",
+ "-Wall",
+ "-Werror",
+ ],
+}
+
cc_library_shared {
name: "libdynproc",
vendor: true,
+ defaults: [
+ "dynamicsprocessingdefaults",
+ ],
+
srcs: [
"EffectDynamicsProcessing.cpp",
- "dsp/DPBase.cpp",
- "dsp/DPFrequency.cpp",
],
cflags: [
"-O2",
"-fvisibility=hidden",
-
- "-Wall",
- "-Werror",
- ],
-
- shared_libs: [
- "libcutils",
- "liblog",
],
relative_install_path: "soundfx",
+}
- header_libs: [
- "libaudioeffects",
- "libeigen",
+cc_library_shared {
+ name: "libdynamicsprocessingaidl",
+
+ srcs: [
+ "aidl/DynamicsProcessing.cpp",
+ "aidl/DynamicsProcessingContext.cpp",
+ ":effectCommonFile",
+ ],
+
+ defaults: [
+ "aidlaudioservice_defaults",
+ "latest_android_hardware_audio_effect_ndk_shared",
+ "latest_android_media_audio_common_types_ndk_shared",
+ "dynamicsprocessingdefaults",
+ ],
+
+ visibility: [
+ "//hardware/interfaces/audio/aidl/default",
],
}
diff --git a/media/libeffects/dynamicsproc/aidl/DynamicsProcessing.cpp b/media/libeffects/dynamicsproc/aidl/DynamicsProcessing.cpp
new file mode 100644
index 0000000..203a27b
--- /dev/null
+++ b/media/libeffects/dynamicsproc/aidl/DynamicsProcessing.cpp
@@ -0,0 +1,313 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "AHAL_DynamicsProcessingLibEffects"
+
+#include <android-base/logging.h>
+
+#include "DynamicsProcessing.h"
+
+#include <dsp/DPBase.h>
+#include <dsp/DPFrequency.h>
+
+using aidl::android::hardware::audio::effect::Descriptor;
+using aidl::android::hardware::audio::effect::DynamicsProcessingImpl;
+using aidl::android::hardware::audio::effect::IEffect;
+using aidl::android::hardware::audio::effect::kDynamicsProcessingImplUUID;
+using aidl::android::hardware::audio::effect::State;
+using aidl::android::media::audio::common::AudioUuid;
+using aidl::android::media::audio::common::PcmType;
+
+extern "C" binder_exception_t createEffect(const AudioUuid* in_impl_uuid,
+ std::shared_ptr<IEffect>* instanceSpp) {
+ if (!in_impl_uuid || *in_impl_uuid != kDynamicsProcessingImplUUID) {
+ LOG(ERROR) << __func__ << "uuid not supported";
+ return EX_ILLEGAL_ARGUMENT;
+ }
+ if (instanceSpp) {
+ *instanceSpp = ndk::SharedRefBase::make<DynamicsProcessingImpl>();
+ LOG(DEBUG) << __func__ << " instance " << instanceSpp->get() << " created";
+ return EX_NONE;
+ } else {
+ LOG(ERROR) << __func__ << " invalid input parameter!";
+ return EX_ILLEGAL_ARGUMENT;
+ }
+}
+
+extern "C" binder_exception_t queryEffect(const AudioUuid* in_impl_uuid, Descriptor* _aidl_return) {
+ if (!in_impl_uuid || *in_impl_uuid != kDynamicsProcessingImplUUID) {
+ LOG(ERROR) << __func__ << "uuid not supported";
+ return EX_ILLEGAL_ARGUMENT;
+ }
+ *_aidl_return = DynamicsProcessingImpl::kDescriptor;
+ return EX_NONE;
+}
+
+namespace aidl::android::hardware::audio::effect {
+
+const std::string DynamicsProcessingImpl::kEffectName = "DynamicsProcessing";
+const DynamicsProcessing::Capability DynamicsProcessingImpl::kCapability = {.minCutOffFreq = 220,
+ .maxCutOffFreq = 20000};
+const Descriptor DynamicsProcessingImpl::kDescriptor = {
+ .common = {.id = {.type = kDynamicsProcessingTypeUUID,
+ .uuid = kDynamicsProcessingImplUUID,
+ .proxy = std::nullopt},
+ .flags = {.type = Flags::Type::INSERT,
+ .insert = Flags::Insert::LAST,
+ .volume = Flags::Volume::CTRL},
+ .name = DynamicsProcessingImpl::kEffectName,
+ .implementor = "The Android Open Source Project"},
+ .capability = Capability::make<Capability::dynamicsProcessing>(
+ DynamicsProcessingImpl::kCapability)};
+
+ndk::ScopedAStatus DynamicsProcessingImpl::open(const Parameter::Common& common,
+ const std::optional<Parameter::Specific>& specific,
+ OpenEffectReturn* ret) {
+ LOG(DEBUG) << __func__;
+ // effect only support 32bits float
+ RETURN_IF(common.input.base.format.pcm != common.output.base.format.pcm ||
+ common.input.base.format.pcm != PcmType::FLOAT_32_BIT,
+ EX_ILLEGAL_ARGUMENT, "dataMustBe32BitsFloat");
+ RETURN_OK_IF(mState != State::INIT);
+ auto context = createContext(common);
+ RETURN_IF(!context, EX_NULL_POINTER, "createContextFailed");
+
+ RETURN_IF_ASTATUS_NOT_OK(setParameterCommon(common), "setCommParamErr");
+ if (specific.has_value()) {
+ RETURN_IF_ASTATUS_NOT_OK(setParameterSpecific(specific.value()), "setSpecParamErr");
+ } else {
+ Parameter::Specific defaultSpecific =
+ Parameter::Specific::make<Parameter::Specific::dynamicsProcessing>(
+ DynamicsProcessing::make<DynamicsProcessing::engineArchitecture>(
+ mContext->getEngineArchitecture()));
+ RETURN_IF_ASTATUS_NOT_OK(setParameterSpecific(defaultSpecific), "setDefaultEngineErr");
+ }
+
+ mState = State::IDLE;
+ context->dupeFmq(ret);
+ RETURN_IF(createThread(context, getEffectName()) != RetCode::SUCCESS, EX_UNSUPPORTED_OPERATION,
+ "FailedToCreateWorker");
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus DynamicsProcessingImpl::getDescriptor(Descriptor* _aidl_return) {
+ RETURN_IF(!_aidl_return, EX_ILLEGAL_ARGUMENT, "Parameter:nullptr");
+ LOG(DEBUG) << __func__ << kDescriptor.toString();
+ *_aidl_return = kDescriptor;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus DynamicsProcessingImpl::commandImpl(CommandId command) {
+ RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+ switch (command) {
+ case CommandId::START:
+ mContext->enable();
+ return ndk::ScopedAStatus::ok();
+ case CommandId::STOP:
+ mContext->disable();
+ return ndk::ScopedAStatus::ok();
+ case CommandId::RESET:
+ mContext->disable();
+ mContext->resetBuffer();
+ return ndk::ScopedAStatus::ok();
+ default:
+ // Need this default handling for vendor extendable CommandId::VENDOR_COMMAND_*
+ LOG(ERROR) << __func__ << " commandId " << toString(command) << " not supported";
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "commandIdNotSupported");
+ }
+}
+
+ndk::ScopedAStatus DynamicsProcessingImpl::setParameterSpecific(
+ const Parameter::Specific& specific) {
+ RETURN_IF(Parameter::Specific::dynamicsProcessing != specific.getTag(), EX_ILLEGAL_ARGUMENT,
+ "EffectNotSupported");
+ RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+
+ auto& param = specific.get<Parameter::Specific::dynamicsProcessing>();
+ auto tag = param.getTag();
+
+ switch (tag) {
+ case DynamicsProcessing::engineArchitecture: {
+ RETURN_IF(mContext->setEngineArchitecture(
+ param.get<DynamicsProcessing::engineArchitecture>()) !=
+ RetCode::SUCCESS,
+ EX_ILLEGAL_ARGUMENT, "setEngineArchitectureFailed");
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::preEq: {
+ RETURN_IF(
+ mContext->setPreEq(param.get<DynamicsProcessing::preEq>()) != RetCode::SUCCESS,
+ EX_ILLEGAL_ARGUMENT, "setPreEqFailed");
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::postEq: {
+ RETURN_IF(mContext->setPostEq(param.get<DynamicsProcessing::postEq>()) !=
+ RetCode::SUCCESS,
+ EX_ILLEGAL_ARGUMENT, "setPostEqFailed");
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::preEqBand: {
+ RETURN_IF(mContext->setPreEqBand(param.get<DynamicsProcessing::preEqBand>()) !=
+ RetCode::SUCCESS,
+ EX_ILLEGAL_ARGUMENT, "setPreEqBandFailed");
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::postEqBand: {
+ RETURN_IF(mContext->setPostEqBand(param.get<DynamicsProcessing::postEqBand>()) !=
+ RetCode::SUCCESS,
+ EX_ILLEGAL_ARGUMENT, "setPostEqBandFailed");
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::mbc: {
+ RETURN_IF(mContext->setMbc(param.get<DynamicsProcessing::mbc>()) != RetCode::SUCCESS,
+ EX_ILLEGAL_ARGUMENT, "setMbcFailed");
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::mbcBand: {
+ RETURN_IF(mContext->setMbcBand(param.get<DynamicsProcessing::mbcBand>()) !=
+ RetCode::SUCCESS,
+ EX_ILLEGAL_ARGUMENT, "setMbcBandFailed");
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::limiter: {
+ RETURN_IF(mContext->setLimiter(param.get<DynamicsProcessing::limiter>()) !=
+ RetCode::SUCCESS,
+ EX_ILLEGAL_ARGUMENT, "setLimiterFailed");
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::inputGain: {
+ RETURN_IF(mContext->setInputGain(param.get<DynamicsProcessing::inputGain>()) !=
+ RetCode::SUCCESS,
+ EX_ILLEGAL_ARGUMENT, "setInputGainFailed");
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::vendorExtension: {
+ LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag);
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+ EX_ILLEGAL_ARGUMENT, "DPVendorExtensionTagNotSupported");
+ }
+ }
+}
+
+ndk::ScopedAStatus DynamicsProcessingImpl::getParameterSpecific(const Parameter::Id& id,
+ Parameter::Specific* specific) {
+ RETURN_IF(!specific, EX_NULL_POINTER, "nullPtr");
+ auto tag = id.getTag();
+ RETURN_IF(Parameter::Id::dynamicsProcessingTag != tag, EX_ILLEGAL_ARGUMENT, "wrongIdTag");
+ auto dpId = id.get<Parameter::Id::dynamicsProcessingTag>();
+ auto dpIdTag = dpId.getTag();
+ switch (dpIdTag) {
+ case DynamicsProcessing::Id::commonTag:
+ return getParameterDynamicsProcessing(dpId.get<DynamicsProcessing::Id::commonTag>(),
+ specific);
+ case DynamicsProcessing::Id::vendorExtensionTag:
+ LOG(ERROR) << __func__ << " unsupported ID: " << toString(dpIdTag);
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+ EX_ILLEGAL_ARGUMENT, "DPVendorExtensionIdNotSupported");
+ }
+}
+
+ndk::ScopedAStatus DynamicsProcessingImpl::getParameterDynamicsProcessing(
+ const DynamicsProcessing::Tag& tag, Parameter::Specific* specific) {
+ RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+
+ switch (tag) {
+ case DynamicsProcessing::engineArchitecture: {
+ specific->set<Parameter::Specific::dynamicsProcessing>(
+ DynamicsProcessing::make<DynamicsProcessing::engineArchitecture>(
+ mContext->getEngineArchitecture()));
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::preEq: {
+ specific->set<Parameter::Specific::dynamicsProcessing>(
+ DynamicsProcessing::make<DynamicsProcessing::preEq>(mContext->getPreEq()));
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::postEq: {
+ specific->set<Parameter::Specific::dynamicsProcessing>(
+ DynamicsProcessing::make<DynamicsProcessing::postEq>(mContext->getPostEq()));
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::preEqBand: {
+ specific->set<Parameter::Specific::dynamicsProcessing>(
+ DynamicsProcessing::make<DynamicsProcessing::preEqBand>(
+ mContext->getPreEqBand()));
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::postEqBand: {
+ specific->set<Parameter::Specific::dynamicsProcessing>(
+ DynamicsProcessing::make<DynamicsProcessing::postEqBand>(
+ mContext->getPostEqBand()));
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::mbc: {
+ specific->set<Parameter::Specific::dynamicsProcessing>(
+ DynamicsProcessing::make<DynamicsProcessing::mbc>(mContext->getMbc()));
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::mbcBand: {
+ specific->set<Parameter::Specific::dynamicsProcessing>(
+ DynamicsProcessing::make<DynamicsProcessing::mbcBand>(mContext->getMbcBand()));
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::limiter: {
+ specific->set<Parameter::Specific::dynamicsProcessing>(
+ DynamicsProcessing::make<DynamicsProcessing::limiter>(mContext->getLimiter()));
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::inputGain: {
+ specific->set<Parameter::Specific::dynamicsProcessing>(
+ DynamicsProcessing::make<DynamicsProcessing::inputGain>(
+ mContext->getInputGain()));
+ return ndk::ScopedAStatus::ok();
+ }
+ case DynamicsProcessing::vendorExtension: {
+ LOG(ERROR) << __func__ << " wrong vendor tag in CommonTag: " << toString(tag);
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+ EX_ILLEGAL_ARGUMENT, "DPVendorExtensionTagInWrongId");
+ }
+ }
+}
+
+std::shared_ptr<EffectContext> DynamicsProcessingImpl::createContext(
+ const Parameter::Common& common) {
+ if (mContext) {
+ LOG(DEBUG) << __func__ << " context already exist";
+ return mContext;
+ }
+
+ mContext = std::make_shared<DynamicsProcessingContext>(1 /* statusFmqDepth */, common);
+ return mContext;
+}
+
+RetCode DynamicsProcessingImpl::releaseContext() {
+ if (mContext) {
+ mContext->disable();
+ mContext->resetBuffer();
+ mContext.reset();
+ }
+ return RetCode::SUCCESS;
+}
+
+// Processing method running in EffectWorker thread.
+IEffect::Status DynamicsProcessingImpl::effectProcessImpl(float* in, float* out, int samples) {
+ IEffect::Status status = {EX_NULL_POINTER, 0, 0};
+ RETURN_VALUE_IF(!mContext, status, "nullContext");
+ return mContext->lvmProcess(in, out, samples);
+}
+
+} // namespace aidl::android::hardware::audio::effect
diff --git a/media/libeffects/dynamicsproc/aidl/DynamicsProcessing.h b/media/libeffects/dynamicsproc/aidl/DynamicsProcessing.h
new file mode 100644
index 0000000..824ebea
--- /dev/null
+++ b/media/libeffects/dynamicsproc/aidl/DynamicsProcessing.h
@@ -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 <aidl/android/hardware/audio/effect/BnEffect.h>
+
+#include "effect-impl/EffectImpl.h"
+#include "effect-impl/EffectUUID.h"
+#include "DynamicsProcessingContext.h"
+
+namespace aidl::android::hardware::audio::effect {
+
+class DynamicsProcessingImpl final : public EffectImpl {
+ public:
+ static const std::string kEffectName;
+ static const Descriptor kDescriptor;
+ static const DynamicsProcessing::Capability kCapability;
+
+ DynamicsProcessingImpl() { LOG(DEBUG) << __func__; }
+ ~DynamicsProcessingImpl() {
+ cleanUp();
+ LOG(DEBUG) << __func__;
+ }
+
+ ndk::ScopedAStatus open(const Parameter::Common& common,
+ const std::optional<Parameter::Specific>& specific,
+ OpenEffectReturn* ret) override;
+ ndk::ScopedAStatus commandImpl(CommandId command) override;
+ ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
+ ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific) override;
+ ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id,
+ Parameter::Specific* specific) override;
+ IEffect::Status effectProcessImpl(float* in, float* out, int process) override;
+ std::shared_ptr<EffectContext> createContext(const Parameter::Common& common) override;
+ RetCode releaseContext() override;
+
+ std::shared_ptr<EffectContext> getContext() override { return mContext; }
+ std::string getEffectName() override { return kEffectName; }
+
+ private:
+ std::shared_ptr<DynamicsProcessingContext> mContext;
+ ndk::ScopedAStatus getParameterDynamicsProcessing(const DynamicsProcessing::Tag& tag,
+ Parameter::Specific* specific);
+};
+
+} // namespace aidl::android::hardware::audio::effect
diff --git a/media/libeffects/dynamicsproc/aidl/DynamicsProcessingContext.cpp b/media/libeffects/dynamicsproc/aidl/DynamicsProcessingContext.cpp
new file mode 100644
index 0000000..57a2be9
--- /dev/null
+++ b/media/libeffects/dynamicsproc/aidl/DynamicsProcessingContext.cpp
@@ -0,0 +1,580 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "AHAL_DPLibEffectsContext"
+
+#include "DynamicsProcessing.h"
+#include "DynamicsProcessingContext.h"
+
+#include <functional>
+#include <sys/param.h>
+#include <unordered_set>
+
+namespace aidl::android::hardware::audio::effect {
+
+DynamicsProcessingContext::DynamicsProcessingContext(int statusDepth,
+ const Parameter::Common& common)
+ : EffectContext(statusDepth, common) {
+ LOG(DEBUG) << __func__;
+ init();
+}
+
+DynamicsProcessingContext::~DynamicsProcessingContext() {
+ LOG(DEBUG) << __func__;
+}
+
+RetCode DynamicsProcessingContext::enable() {
+ std::lock_guard lg(mMutex);
+ if (mState != DYNAMICS_PROCESSING_STATE_INITIALIZED) {
+ return RetCode::ERROR_EFFECT_LIB_ERROR;
+ }
+ mState = DYNAMICS_PROCESSING_STATE_ACTIVE;
+ return RetCode::SUCCESS;
+}
+
+RetCode DynamicsProcessingContext::disable() {
+ std::lock_guard lg(mMutex);
+ if (mState != DYNAMICS_PROCESSING_STATE_ACTIVE) {
+ return RetCode::ERROR_EFFECT_LIB_ERROR;
+ }
+ mState = DYNAMICS_PROCESSING_STATE_INITIALIZED;
+ return RetCode::SUCCESS;
+}
+
+void DynamicsProcessingContext::reset() {
+ std::lock_guard lg(mMutex);
+ if (mDpFreq != nullptr) {
+ mDpFreq.reset();
+ }
+}
+
+RetCode DynamicsProcessingContext::setCommon(const Parameter::Common& common) {
+ mCommon = common;
+ init();
+ return RetCode::SUCCESS;
+}
+
+void DynamicsProcessingContext::dpSetFreqDomainVariant_l(
+ const DynamicsProcessing::EngineArchitecture& engine) {
+ mDpFreq.reset(new dp_fx::DPFrequency());
+ mDpFreq->init(mChannelCount, engine.preEqStage.inUse, engine.preEqStage.bandCount,
+ engine.mbcStage.inUse, engine.mbcStage.bandCount, engine.postEqStage.inUse,
+ engine.postEqStage.bandCount, engine.limiterInUse);
+
+ int32_t sampleRate = mCommon.input.base.sampleRate;
+ int32_t minBlockSize = (int32_t)dp_fx::DPFrequency::getMinBockSize();
+ int32_t block = engine.preferredProcessingDurationMs * sampleRate / 1000.0f;
+ LOG(INFO) << __func__ << " sampleRate " << sampleRate << " block length "
+ << engine.preferredProcessingDurationMs << " ms (" << block << "samples)";
+ if (block < minBlockSize) {
+ block = minBlockSize;
+ } else if (!powerof2(block)) {
+ //find next highest power of 2.
+ block = 1 << (32 - __builtin_clz(block));
+ }
+ mDpFreq->configure(block, block >> 1, sampleRate);
+}
+
+RetCode DynamicsProcessingContext::setEngineArchitecture(
+ const DynamicsProcessing::EngineArchitecture& engineArchitecture) {
+ RETURN_VALUE_IF(!validateEngineConfig(engineArchitecture), RetCode::ERROR_ILLEGAL_PARAMETER,
+ "illegalEngineConfig");
+
+ std::lock_guard lg(mMutex);
+ if (!mEngineInited || mEngineArchitecture != engineArchitecture) {
+ if (engineArchitecture.resolutionPreference ==
+ DynamicsProcessing::ResolutionPreference::FAVOR_FREQUENCY_RESOLUTION) {
+ dpSetFreqDomainVariant_l(engineArchitecture);
+ } else {
+ LOG(WARNING) << __func__ << toString(engineArchitecture.resolutionPreference)
+ << " not available now";
+ }
+ mEngineInited = true;
+ mEngineArchitecture = engineArchitecture;
+ }
+ LOG(INFO) << __func__ << engineArchitecture.toString();
+ return RetCode::SUCCESS;
+}
+
+RetCode DynamicsProcessingContext::setPreEq(
+ const std::vector<DynamicsProcessing::ChannelConfig>& channels) {
+ std::lock_guard lg(mMutex);
+ return setDpChannels_l<dp_fx::DPEq>(channels, mEngineArchitecture.preEqStage.inUse,
+ StageType::PREEQ);
+}
+
+RetCode DynamicsProcessingContext::setPostEq(
+ const std::vector<DynamicsProcessing::ChannelConfig>& channels) {
+ std::lock_guard lg(mMutex);
+ return setDpChannels_l<dp_fx::DPEq>(channels, mEngineArchitecture.postEqStage.inUse,
+ StageType::POSTEQ);
+}
+
+RetCode DynamicsProcessingContext::setMbc(
+ const std::vector<DynamicsProcessing::ChannelConfig>& channels) {
+ std::lock_guard lg(mMutex);
+ return setDpChannels_l<dp_fx::DPMbc>(channels, mEngineArchitecture.mbcStage.inUse,
+ StageType::MBC);
+}
+
+RetCode DynamicsProcessingContext::setPreEqBand(
+ const std::vector<DynamicsProcessing::EqBandConfig>& bands) {
+ std::lock_guard lg(mMutex);
+ RETURN_VALUE_IF(!mEngineArchitecture.postEqStage.inUse, RetCode::ERROR_ILLEGAL_PARAMETER,
+ "postEqNotInUse");
+ return setBands_l<DynamicsProcessing::EqBandConfig>(
+ bands, mEngineArchitecture.preEqStage.bandCount, StageType::PREEQ);
+}
+
+RetCode DynamicsProcessingContext::setPostEqBand(
+ const std::vector<DynamicsProcessing::EqBandConfig>& bands) {
+ std::lock_guard lg(mMutex);
+ RETURN_VALUE_IF(!mEngineArchitecture.postEqStage.inUse, RetCode::ERROR_ILLEGAL_PARAMETER,
+ "postEqNotInUse");
+ return setBands_l<DynamicsProcessing::EqBandConfig>(
+ bands, mEngineArchitecture.postEqStage.bandCount, StageType::POSTEQ);
+}
+
+RetCode DynamicsProcessingContext::setMbcBand(
+ const std::vector<DynamicsProcessing::MbcBandConfig>& bands) {
+ std::lock_guard lg(mMutex);
+ RETURN_VALUE_IF(!mEngineArchitecture.mbcStage.inUse, RetCode::ERROR_ILLEGAL_PARAMETER,
+ "mbcNotInUse");
+ return setBands_l<DynamicsProcessing::MbcBandConfig>(
+ bands, mEngineArchitecture.preEqStage.bandCount, StageType::MBC);
+}
+
+RetCode DynamicsProcessingContext::setLimiter(
+ const std::vector<DynamicsProcessing::LimiterConfig>& limiters) {
+ std::lock_guard lg(mMutex);
+ RETURN_VALUE_IF(!mEngineArchitecture.limiterInUse, RetCode::ERROR_ILLEGAL_PARAMETER,
+ "limiterNotInUse");
+ return setBands_l<DynamicsProcessing::LimiterConfig>(limiters, -1, StageType::LIMITER);
+}
+
+RetCode DynamicsProcessingContext::setInputGain(
+ const std::vector<DynamicsProcessing::InputGain>& inputGains) {
+ std::lock_guard lg(mMutex);
+ return setBands_l<DynamicsProcessing::InputGain>(inputGains, -1, StageType::INPUTGAIN);
+}
+
+DynamicsProcessing::EngineArchitecture DynamicsProcessingContext::getEngineArchitecture() {
+ std::lock_guard lg(mMutex);
+ LOG(INFO) << __func__ << mEngineArchitecture.toString();
+ return mEngineArchitecture;
+}
+
+std::vector<DynamicsProcessing::ChannelConfig> DynamicsProcessingContext::getPreEq() {
+ return getChannelConfig(StageType::PREEQ);
+}
+
+std::vector<DynamicsProcessing::ChannelConfig> DynamicsProcessingContext::getPostEq() {
+ return getChannelConfig(StageType::POSTEQ);
+}
+
+std::vector<DynamicsProcessing::EqBandConfig> DynamicsProcessingContext::getPreEqBand() {
+ return getEqBandConfigs(StageType::PREEQ);
+}
+
+std::vector<DynamicsProcessing::EqBandConfig> DynamicsProcessingContext::getPostEqBand() {
+ return getEqBandConfigs(StageType::POSTEQ);
+}
+
+std::vector<DynamicsProcessing::ChannelConfig> DynamicsProcessingContext::getMbc() {
+ return getChannelConfig(StageType::MBC);
+}
+
+std::vector<DynamicsProcessing::MbcBandConfig> DynamicsProcessingContext::getMbcBand() {
+ std::vector<DynamicsProcessing::MbcBandConfig> bands;
+
+ std::lock_guard lg(mMutex);
+ auto maxBand = mEngineArchitecture.mbcStage.bandCount;
+ for (int32_t ch = 0; ch < mChannelCount; ch++) {
+ auto mbc = getMbc_l(ch);
+ if (!mbc) {
+ continue;
+ }
+ for (int32_t bandId = 0; bandId < maxBand; bandId++) {
+ auto band = mbc->getBand(bandId);
+ if (!band) {
+ continue;
+ }
+ bands.push_back({.channel = ch,
+ .band = bandId,
+ .enable = band->isEnabled(),
+ .cutoffFrequencyHz = band->getCutoffFrequency(),
+ .attackTimeMs = band->getAttackTime(),
+ .releaseTimeMs = band->getReleaseTime(),
+ .ratio = band->getRatio(),
+ .thresholdDb = band->getThreshold(),
+ .kneeWidthDb = band->getKneeWidth(),
+ .noiseGateThresholdDb = band->getNoiseGateThreshold(),
+ .expanderRatio = band->getExpanderRatio(),
+ .preGainDb = band->getPreGain(),
+ .postGainDb = band->getPostGain()});
+ }
+ }
+ return bands;
+}
+
+std::vector<DynamicsProcessing::LimiterConfig> DynamicsProcessingContext::getLimiter() {
+ std::vector<DynamicsProcessing::LimiterConfig> ret;
+
+ std::lock_guard lg(mMutex);
+ for (int32_t ch = 0; ch < mChannelCount; ch++) {
+ auto limiter = getLimiter_l(ch);
+ if (!limiter) {
+ continue;
+ }
+ ret.push_back({.channel = ch,
+ .enable = limiter->isEnabled(),
+ .linkGroup = static_cast<int32_t>(limiter->getLinkGroup()),
+ .attackTimeMs = limiter->getAttackTime(),
+ .releaseTimeMs = limiter->getReleaseTime(),
+ .ratio = limiter->getRatio(),
+ .thresholdDb = limiter->getThreshold(),
+ .postGainDb = limiter->getPostGain()});
+ }
+ return ret;
+}
+
+std::vector<DynamicsProcessing::InputGain> DynamicsProcessingContext::getInputGain() {
+ std::vector<DynamicsProcessing::InputGain> ret;
+
+ std::lock_guard lg(mMutex);
+ for (int32_t ch = 0; ch < mChannelCount; ch++) {
+ auto channel = getChannel_l(ch);
+ if (!channel) {
+ continue;
+ }
+ ret.push_back({.channel = ch, .gainDb = channel->getInputGain()});
+ }
+ return ret;
+}
+
+IEffect::Status DynamicsProcessingContext::lvmProcess(float* in, float* out, int samples) {
+ LOG(DEBUG) << __func__ << " in " << in << " out " << out << " sample " << samples;
+
+ IEffect::Status status = {EX_NULL_POINTER, 0, 0};
+ RETURN_VALUE_IF(!in, status, "nullInput");
+ RETURN_VALUE_IF(!out, status, "nullOutput");
+ status = {EX_ILLEGAL_STATE, 0, 0};
+
+ LOG(DEBUG) << __func__ << " start processing";
+ {
+ std::lock_guard lg(mMutex);
+ RETURN_VALUE_IF(mState != DynamicsProcessingState::DYNAMICS_PROCESSING_STATE_ACTIVE, status,
+ "notInActiveState");
+ RETURN_VALUE_IF(!mDpFreq, status, "engineNotInited");
+ mDpFreq->processSamples(in, out, samples);
+ }
+ return {STATUS_OK, samples, samples};
+}
+
+void DynamicsProcessingContext::init() {
+ std::lock_guard lg(mMutex);
+ mState = DYNAMICS_PROCESSING_STATE_INITIALIZED;
+ mChannelCount =
+ ::android::hardware::audio::common::getChannelCount(mCommon.input.base.channelMask);
+}
+
+dp_fx::DPChannel* DynamicsProcessingContext::getChannel_l(int channel) {
+ RETURN_VALUE_IF(mDpFreq == nullptr, nullptr, "DPFreqNotInited");
+
+ return mDpFreq->getChannel(channel);
+}
+
+dp_fx::DPEq* DynamicsProcessingContext::getPreEq_l(int ch) {
+ auto channel = getChannel_l(ch);
+ RETURN_VALUE_IF(channel == nullptr, nullptr, "ChannelNotExist");
+
+ return channel->getPreEq();
+}
+
+dp_fx::DPEq* DynamicsProcessingContext::getPostEq_l(int ch) {
+ auto channel = getChannel_l(ch);
+ RETURN_VALUE_IF(channel == nullptr, nullptr, "ChannelNotExist");
+
+ return channel->getPostEq();
+}
+
+dp_fx::DPMbc* DynamicsProcessingContext::getMbc_l(int ch) {
+ auto channel = getChannel_l(ch);
+ RETURN_VALUE_IF(channel == nullptr, nullptr, "ChannelNotExist");
+
+ return channel->getMbc();
+}
+
+dp_fx::DPLimiter* DynamicsProcessingContext::getLimiter_l(int ch) {
+ auto channel = getChannel_l(ch);
+ RETURN_VALUE_IF(channel == nullptr, nullptr, "ChannelNotExist");
+
+ return channel->getLimiter();
+}
+
+dp_fx::DPBandStage* DynamicsProcessingContext::getStageWithType_l(
+ DynamicsProcessingContext::StageType type, int ch) {
+ switch (type) {
+ case StageType::PREEQ: {
+ return getEqWithType_l(type, ch);
+ }
+ case StageType::POSTEQ: {
+ return getEqWithType_l(type, ch);
+ }
+ case StageType::MBC: {
+ return getMbc_l(ch);
+ }
+ case StageType::LIMITER:
+ FALLTHROUGH_INTENDED;
+ case StageType::INPUTGAIN: {
+ return nullptr;
+ }
+ }
+}
+
+dp_fx::DPEq* DynamicsProcessingContext::getEqWithType_l(DynamicsProcessingContext::StageType type,
+ int ch) {
+ switch (type) {
+ case StageType::PREEQ: {
+ return getPreEq_l(ch);
+ }
+ case StageType::POSTEQ: {
+ return getPostEq_l(ch);
+ }
+ case StageType::MBC:
+ FALLTHROUGH_INTENDED;
+ case StageType::LIMITER:
+ FALLTHROUGH_INTENDED;
+ case StageType::INPUTGAIN: {
+ return nullptr;
+ }
+ }
+}
+
+std::vector<DynamicsProcessing::ChannelConfig> DynamicsProcessingContext::getChannelConfig(
+ StageType type) {
+ std::vector<DynamicsProcessing::ChannelConfig> ret;
+
+ std::lock_guard lg(mMutex);
+ for (int32_t ch = 0; ch < mChannelCount; ch++) {
+ auto stage = getStageWithType_l(type, ch);
+ if (!stage) {
+ continue;
+ }
+ ret.push_back({.channel = ch, .enable = stage->isEnabled()});
+ }
+ return ret;
+}
+
+std::vector<DynamicsProcessing::EqBandConfig> DynamicsProcessingContext::getEqBandConfigs(
+ StageType type) {
+ std::vector<DynamicsProcessing::EqBandConfig> eqBands;
+
+ std::lock_guard lg(mMutex);
+ auto maxBand = mEngineArchitecture.preEqStage.bandCount;
+ for (int32_t ch = 0; ch < mChannelCount; ch++) {
+ auto eq = getEqWithType_l(type, ch);
+ if (!eq) {
+ continue;
+ }
+ for (int32_t bandId = 0; bandId < maxBand; bandId++) {
+ auto band = eq->getBand(bandId);
+ if (!band) {
+ continue;
+ }
+ eqBands.push_back({.channel = ch,
+ .band = bandId,
+ .enable = band->isEnabled(),
+ .cutoffFrequencyHz = band->getCutoffFrequency(),
+ .gainDb = band->getGain()});
+ }
+ }
+ return eqBands;
+}
+
+/**
+ * When StageEnablement is in use, bandCount needs to be positive.
+ */
+bool DynamicsProcessingContext::validateStageEnablement(
+ const DynamicsProcessing::StageEnablement& enablement) {
+ return !enablement.inUse || (enablement.inUse && enablement.bandCount > 0);
+}
+
+bool DynamicsProcessingContext::validateEngineConfig(
+ const DynamicsProcessing::EngineArchitecture& engine) {
+ return engine.preferredProcessingDurationMs >= 0 &&
+ validateStageEnablement(engine.preEqStage) &&
+ validateStageEnablement(engine.postEqStage) && validateStageEnablement(engine.mbcStage);
+}
+
+inline bool DynamicsProcessingContext::validateCutoffFrequency(float freq) {
+ return freq >= DynamicsProcessingImpl::kCapability.minCutOffFreq &&
+ freq <= DynamicsProcessingImpl::kCapability.maxCutOffFreq;
+}
+
+bool DynamicsProcessingContext::validateEqBandConfig(const DynamicsProcessing::EqBandConfig& band,
+ int maxChannel, int maxBand) {
+ return validateChannel(band.channel, maxChannel) && validateBand(band.band, maxBand) &&
+ validateCutoffFrequency(band.cutoffFrequencyHz);
+}
+
+bool DynamicsProcessingContext::validateMbcBandConfig(const DynamicsProcessing::MbcBandConfig& band,
+ int maxChannel, int maxBand) {
+ return validateChannel(band.channel, maxChannel) && validateBand(band.band, maxBand) &&
+ validateCutoffFrequency(band.cutoffFrequencyHz) && validateTime(band.attackTimeMs) &&
+ validateTime(band.releaseTimeMs) && validateRatio(band.ratio) &&
+ validateBandDb(band.thresholdDb) && validateBandDb(band.kneeWidthDb) &&
+ validateBandDb(band.noiseGateThresholdDb) && validateRatio(band.expanderRatio);
+}
+
+bool DynamicsProcessingContext::validateLimiterConfig(
+ const DynamicsProcessing::LimiterConfig& limiter, int maxChannel) {
+ return validateChannel(limiter.channel, maxChannel) && validateTime(limiter.attackTimeMs) &&
+ validateTime(limiter.releaseTimeMs) && validateRatio(limiter.ratio) &&
+ validateBandDb(limiter.thresholdDb);
+}
+
+bool DynamicsProcessingContext::validateInputGainConfig(const DynamicsProcessing::InputGain& gain,
+ int maxChannel) {
+ return validateChannel(gain.channel, maxChannel);
+}
+
+template <typename D>
+RetCode DynamicsProcessingContext::setDpChannels_l(
+ const std::vector<DynamicsProcessing::ChannelConfig>& channels, bool stageInUse,
+ StageType type) {
+ RetCode ret = RetCode::SUCCESS;
+ std::unordered_set<int> channelSet;
+
+ RETURN_VALUE_IF(!stageInUse, RetCode::ERROR_ILLEGAL_PARAMETER, "stageNotInUse");
+ for (auto& it : channels) {
+ if (0 != channelSet.count(it.channel)) {
+ LOG(WARNING) << __func__ << " duplicated channel " << it.channel;
+ ret = RetCode::ERROR_ILLEGAL_PARAMETER;
+ } else {
+ channelSet.insert(it.channel);
+ }
+ if (it.channel < 0 || it.channel >= mChannelCount) {
+ LOG(WARNING) << __func__ << " skip illegal ChannelConfig " << it.toString() << " max "
+ << mChannelCount;
+ ret = RetCode::ERROR_ILLEGAL_PARAMETER;
+ continue;
+ }
+ auto dp = getStageWithType_l(type, it.channel);
+ if (!dp) {
+ LOG(WARNING) << __func__ << " channel " << it.channel << " not exist";
+ ret = RetCode::ERROR_ILLEGAL_PARAMETER;
+ continue;
+ }
+ if (dp->isEnabled() != it.enable) {
+ LOG(INFO) << __func__ << it.toString();
+ dp->setEnabled(it.enable);
+ }
+ }
+ return ret;
+}
+
+RetCode DynamicsProcessingContext::setDpChannelBand_l(const std::any& anyConfig, StageType type,
+ int maxCh, int maxBand,
+ std::set<std::pair<int, int>>& chBandSet) {
+ RETURN_VALUE_IF(!anyConfig.has_value(), RetCode::ERROR_ILLEGAL_PARAMETER, "bandInvalid");
+ RetCode ret = RetCode::SUCCESS;
+ std::pair<int, int> chBandKey;
+ switch (type) {
+ case StageType::PREEQ:
+ FALLTHROUGH_INTENDED;
+ case StageType::POSTEQ: {
+ dp_fx::DPEq* dp;
+ const auto& config = std::any_cast<DynamicsProcessing::EqBandConfig>(anyConfig);
+ RETURN_VALUE_IF(!validateEqBandConfig(config, maxCh, maxBand),
+ RetCode::ERROR_ILLEGAL_PARAMETER, "eqBandNotValid");
+ RETURN_VALUE_IF(
+ nullptr == (dp = getEqWithType_l(type, config.channel)) || !dp->isEnabled(),
+ RetCode::ERROR_ILLEGAL_PARAMETER, "dpEqNotExist");
+ dp_fx::DPEqBand band;
+ band.init(config.enable, config.cutoffFrequencyHz, config.gainDb);
+ dp->setBand(config.band, band);
+ chBandKey = {config.channel, config.band};
+ break;
+ }
+ case StageType::MBC: {
+ dp_fx::DPMbc* dp;
+ const auto& config = std::any_cast<DynamicsProcessing::MbcBandConfig>(anyConfig);
+ RETURN_VALUE_IF(!validateMbcBandConfig(config, maxCh, maxBand),
+ RetCode::ERROR_ILLEGAL_PARAMETER, "mbcBandNotValid");
+ RETURN_VALUE_IF(nullptr == (dp = getMbc_l(config.channel)) || !dp->isEnabled(),
+ RetCode::ERROR_ILLEGAL_PARAMETER, "dpMbcNotExist");
+ dp_fx::DPMbcBand band;
+ band.init(config.enable, config.cutoffFrequencyHz, config.attackTimeMs,
+ config.releaseTimeMs, config.ratio, config.thresholdDb, config.kneeWidthDb,
+ config.noiseGateThresholdDb, config.expanderRatio, config.preGainDb,
+ config.postGainDb);
+ dp->setBand(config.band, band);
+ chBandKey = {config.channel, config.band};
+ break;
+ }
+ case StageType::LIMITER: {
+ dp_fx::DPChannel* dp;
+ const auto& config = std::any_cast<DynamicsProcessing::LimiterConfig>(anyConfig);
+ RETURN_VALUE_IF(!validateLimiterConfig(config, maxCh),
+ RetCode::ERROR_ILLEGAL_PARAMETER, "limiterBandNotValid");
+ RETURN_VALUE_IF(nullptr == (dp = getChannel_l(config.channel)),
+ RetCode::ERROR_ILLEGAL_PARAMETER, "dpChNotExist");
+ dp_fx::DPLimiter limiter;
+ limiter.init(mEngineArchitecture.limiterInUse, config.enable, config.linkGroup,
+ config.attackTimeMs, config.releaseTimeMs, config.ratio,
+ config.thresholdDb, config.postGainDb);
+ dp->setLimiter(limiter);
+ chBandKey = {config.channel, 0};
+ break;
+ }
+ case StageType::INPUTGAIN: {
+ dp_fx::DPChannel* dp;
+ const auto& config = std::any_cast<DynamicsProcessing::InputGain>(anyConfig);
+ RETURN_VALUE_IF(!validateInputGainConfig(config, maxCh),
+ RetCode::ERROR_ILLEGAL_PARAMETER, "inputGainNotValid");
+ RETURN_VALUE_IF(nullptr == (dp = getChannel_l(config.channel)),
+ RetCode::ERROR_ILLEGAL_PARAMETER, "dpChNotExist");
+ dp->setInputGain(config.gainDb);
+ chBandKey = {config.channel, 0};
+ break;
+ }
+ }
+ RETURN_VALUE_IF(0 != chBandSet.count(chBandKey), RetCode::ERROR_ILLEGAL_PARAMETER,
+ "duplicatedBand");
+ chBandSet.insert(chBandKey);
+ return ret;
+}
+
+template <typename T /* BandConfig */>
+RetCode DynamicsProcessingContext::setBands_l(
+ const std::vector<T>& bands, int maxBand, StageType type) {
+ RetCode ret = RetCode::SUCCESS;
+ std::set<std::pair<int /* channel */, int /* band */>> bandSet;
+
+ for (const auto& it : bands) {
+ if (RetCode::SUCCESS !=
+ setDpChannelBand_l(std::make_any<T>(it), type, mChannelCount, maxBand, bandSet)) {
+ LOG(WARNING) << __func__ << " skipping band " << it.toString();
+ ret = RetCode::ERROR_ILLEGAL_PARAMETER;
+ continue;
+ }
+ LOG(INFO) << __func__ << it.toString();
+ }
+ return ret;
+}
+
+} // namespace aidl::android::hardware::audio::effect
diff --git a/media/libeffects/dynamicsproc/aidl/DynamicsProcessingContext.h b/media/libeffects/dynamicsproc/aidl/DynamicsProcessingContext.h
new file mode 100644
index 0000000..8be784e
--- /dev/null
+++ b/media/libeffects/dynamicsproc/aidl/DynamicsProcessingContext.h
@@ -0,0 +1,130 @@
+/*
+ * 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 <android-base/thread_annotations.h>
+#include <audio_effects/effect_dynamicsprocessing.h>
+
+#include "effect-impl/EffectContext.h"
+
+#include <any>
+#include <cstddef>
+#include <dsp/DPBase.h>
+#include <dsp/DPFrequency.h>
+
+namespace aidl::android::hardware::audio::effect {
+
+enum DynamicsProcessingState {
+ DYNAMICS_PROCESSING_STATE_UNINITIALIZED,
+ DYNAMICS_PROCESSING_STATE_INITIALIZED,
+ DYNAMICS_PROCESSING_STATE_ACTIVE,
+};
+
+class DynamicsProcessingContext final : public EffectContext {
+ public:
+ DynamicsProcessingContext(int statusDepth, const Parameter::Common& common);
+ ~DynamicsProcessingContext();
+
+ RetCode enable();
+ RetCode disable();
+ void reset();
+
+ // override EffectContext::setCommon to update mChannelCount
+ RetCode setCommon(const Parameter::Common& common) override;
+
+ RetCode setEngineArchitecture(const DynamicsProcessing::EngineArchitecture& engineArchitecture);
+ RetCode setPreEq(const std::vector<DynamicsProcessing::ChannelConfig>& eqChannels);
+ RetCode setPostEq(const std::vector<DynamicsProcessing::ChannelConfig>& eqChannels);
+ RetCode setPreEqBand(const std::vector<DynamicsProcessing::EqBandConfig>& eqBands);
+ RetCode setPostEqBand(const std::vector<DynamicsProcessing::EqBandConfig>& eqBands);
+ RetCode setMbc(const std::vector<DynamicsProcessing::ChannelConfig>& mbcChannels);
+ RetCode setMbcBand(const std::vector<DynamicsProcessing::MbcBandConfig>& eqBands);
+ RetCode setLimiter(const std::vector<DynamicsProcessing::LimiterConfig>& limiters);
+ RetCode setInputGain(const std::vector<DynamicsProcessing::InputGain>& gain);
+
+ DynamicsProcessing::EngineArchitecture getEngineArchitecture();
+ std::vector<DynamicsProcessing::ChannelConfig> getPreEq();
+ std::vector<DynamicsProcessing::ChannelConfig> getPostEq();
+ std::vector<DynamicsProcessing::EqBandConfig> getPreEqBand();
+ std::vector<DynamicsProcessing::EqBandConfig> getPostEqBand();
+ std::vector<DynamicsProcessing::ChannelConfig> getMbc();
+ std::vector<DynamicsProcessing::MbcBandConfig> getMbcBand();
+ std::vector<DynamicsProcessing::LimiterConfig> getLimiter();
+ std::vector<DynamicsProcessing::InputGain> getInputGain();
+
+ IEffect::Status lvmProcess(float* in, float* out, int samples);
+
+ private:
+ static constexpr float kPreferredProcessingDurationMs = 10.0f;
+ static constexpr int kBandCount = 5;
+ std::mutex mMutex;
+ size_t mChannelCount GUARDED_BY(mMutex) = 0;
+ DynamicsProcessingState mState GUARDED_BY(mMutex) = DYNAMICS_PROCESSING_STATE_UNINITIALIZED;
+ std::unique_ptr<dp_fx::DPFrequency> mDpFreq GUARDED_BY(mMutex) = nullptr;
+ bool mEngineInited GUARDED_BY(mMutex) = false;
+ DynamicsProcessing::EngineArchitecture mEngineArchitecture GUARDED_BY(mMutex) = {
+ .resolutionPreference =
+ DynamicsProcessing::ResolutionPreference::FAVOR_FREQUENCY_RESOLUTION,
+ .preferredProcessingDurationMs = kPreferredProcessingDurationMs,
+ .preEqStage = {.inUse = true, .bandCount = kBandCount},
+ .postEqStage = {.inUse = true, .bandCount = kBandCount},
+ .mbcStage = {.inUse = true, .bandCount = kBandCount},
+ .limiterInUse = true,
+ };
+
+ enum class StageType { PREEQ, POSTEQ, MBC, LIMITER, INPUTGAIN };
+
+ void init();
+
+ void dpSetFreqDomainVariant_l(const DynamicsProcessing::EngineArchitecture& engine)
+ REQUIRES(mMutex);
+ dp_fx::DPChannel* getChannel_l(int ch) REQUIRES(mMutex);
+ dp_fx::DPEq* getPreEq_l(int ch) REQUIRES(mMutex);
+ dp_fx::DPEq* getPostEq_l(int ch) REQUIRES(mMutex);
+ dp_fx::DPMbc* getMbc_l(int ch) REQUIRES(mMutex);
+ dp_fx::DPLimiter* getLimiter_l(int ch) REQUIRES(mMutex);
+ dp_fx::DPBandStage* getStageWithType_l(StageType type, int ch) REQUIRES(mMutex);
+ dp_fx::DPEq* getEqWithType_l(StageType type, int ch) REQUIRES(mMutex);
+ template <typename D>
+ RetCode setDpChannels_l(const std::vector<DynamicsProcessing::ChannelConfig>& channels,
+ bool stageInUse, StageType type) REQUIRES(mMutex);
+ template <typename T /* BandConfig */>
+ RetCode setBands_l(const std::vector<T>& bands, int maxBand, StageType type) REQUIRES(mMutex);
+ RetCode setDpChannelBand_l(const std::any& anyConfig, StageType type, int maxCh, int maxBand,
+ std::set<std::pair<int, int>>& chBandSet) REQUIRES(mMutex);
+
+ std::vector<DynamicsProcessing::EqBandConfig> getEqBandConfigs(StageType type);
+ std::vector<DynamicsProcessing::ChannelConfig> getChannelConfig(StageType type);
+
+ bool validateStageEnablement(const DynamicsProcessing::StageEnablement& enablement);
+ bool validateEngineConfig(const DynamicsProcessing::EngineArchitecture& engine);
+ bool validateEqBandConfig(const DynamicsProcessing::EqBandConfig& band, int maxChannel,
+ int maxBand);
+ bool validateMbcBandConfig(const DynamicsProcessing::MbcBandConfig& band, int maxChannel,
+ int maxBand);
+ bool validateLimiterConfig(const DynamicsProcessing::LimiterConfig& limiter, int maxChannel);
+ bool validateInputGainConfig(const DynamicsProcessing::InputGain& gain, int maxChannel);
+
+ inline bool validateCutoffFrequency(float freq);
+ inline bool validateChannel(int ch, int maxCh) { return ch >= 0 && ch < maxCh; }
+ inline bool validateBand(int band, int maxBand) { return band >= 0 && band < maxBand; }
+ inline bool validateTime(int time) { return time >= 0; }
+ inline bool validateRatio(int ratio) { return ratio >= 0; }
+ inline bool validateBandDb(int db) { return db <= 0; }
+};
+
+} // namespace aidl::android::hardware::audio::effect
\ No newline at end of file
diff --git a/media/libeffects/hapticgenerator/Android.bp b/media/libeffects/hapticgenerator/Android.bp
index ba511fe..07ba492 100644
--- a/media/libeffects/hapticgenerator/Android.bp
+++ b/media/libeffects/hapticgenerator/Android.bp
@@ -22,6 +22,23 @@
default_applicable_licenses: ["frameworks_av_license"],
}
+cc_defaults {
+ name : "hapticgeneratordefaults",
+ srcs: [
+ "Processors.cpp",
+ ],
+ shared_libs: [
+ "libaudioutils",
+ "libbase",
+ "liblog",
+ "libutils",
+ "libvibratorutils",
+ ],
+ header_libs: [
+ "libaudioeffects",
+ ],
+}
+
cc_library_shared {
name: "libhapticgenerator",
@@ -29,7 +46,10 @@
srcs: [
"EffectHapticGenerator.cpp",
- "Processors.cpp",
+ ],
+
+ defaults: [
+ "hapticgeneratordefaults",
],
cflags: [
@@ -43,17 +63,30 @@
"-fvisibility=hidden",
],
- shared_libs: [
- "libaudioutils",
- "libbase",
- "liblog",
- "libutils",
- "libvibratorutils",
+ relative_install_path: "soundfx",
+}
+
+cc_library_shared {
+ name: "libhapticgeneratoraidl",
+
+ srcs: [
+ "aidl/EffectHapticGenerator.cpp",
+ "aidl/HapticGeneratorContext.cpp",
+ ":effectCommonFile",
],
- relative_install_path: "soundfx",
+ defaults: [
+ "aidlaudioservice_defaults",
+ "latest_android_hardware_audio_effect_ndk_shared",
+ "latest_android_media_audio_common_types_ndk_shared",
+ "hapticgeneratordefaults",
+ ],
- header_libs: [
- "libaudioeffects",
+ cflags: [
+ "-Wthread-safety",
+ ],
+
+ visibility: [
+ "//hardware/interfaces/audio/aidl/default",
],
}
diff --git a/media/libeffects/hapticgenerator/aidl/EffectHapticGenerator.cpp b/media/libeffects/hapticgenerator/aidl/EffectHapticGenerator.cpp
new file mode 100644
index 0000000..7e22482
--- /dev/null
+++ b/media/libeffects/hapticgenerator/aidl/EffectHapticGenerator.cpp
@@ -0,0 +1,189 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "AHAL_HapticGeneratorImpl"
+
+#include "EffectHapticGenerator.h"
+
+#include <android-base/logging.h>
+#include <audio_effects/effect_hapticgenerator.h>
+
+using aidl::android::hardware::audio::effect::Descriptor;
+using aidl::android::hardware::audio::effect::HapticGeneratorImpl;
+using aidl::android::hardware::audio::effect::IEffect;
+using aidl::android::hardware::audio::effect::kHapticGeneratorImplUUID;
+using aidl::android::media::audio::common::AudioUuid;
+
+extern "C" binder_exception_t createEffect(const AudioUuid* in_impl_uuid,
+ std::shared_ptr<IEffect>* instanceSpp) {
+ if (!in_impl_uuid || *in_impl_uuid != kHapticGeneratorImplUUID) {
+ LOG(ERROR) << __func__ << "uuid not supported";
+ return EX_ILLEGAL_ARGUMENT;
+ }
+ if (instanceSpp) {
+ *instanceSpp = ndk::SharedRefBase::make<HapticGeneratorImpl>();
+ LOG(DEBUG) << __func__ << " instance " << instanceSpp->get() << " created";
+ return EX_NONE;
+ } else {
+ LOG(ERROR) << __func__ << " invalid input parameter!";
+ return EX_ILLEGAL_ARGUMENT;
+ }
+}
+
+extern "C" binder_exception_t queryEffect(const AudioUuid* in_impl_uuid, Descriptor* _aidl_return) {
+ if (!in_impl_uuid || *in_impl_uuid != kHapticGeneratorImplUUID) {
+ LOG(ERROR) << __func__ << "uuid not supported";
+ return EX_ILLEGAL_ARGUMENT;
+ }
+ *_aidl_return = HapticGeneratorImpl::kDescriptor;
+ return EX_NONE;
+}
+
+namespace aidl::android::hardware::audio::effect {
+
+const std::string HapticGeneratorImpl::kEffectName = "Haptic Generator";
+const Descriptor HapticGeneratorImpl::kDescriptor = {
+ .common = {.id = {.type = kHapticGeneratorTypeUUID,
+ .uuid = kHapticGeneratorImplUUID,
+ .proxy = std::nullopt},
+ .flags = {.type = Flags::Type::INSERT, .insert = Flags::Insert::FIRST},
+ .name = HapticGeneratorImpl::kEffectName,
+ .implementor = "The Android Open Source Project"}};
+
+ndk::ScopedAStatus HapticGeneratorImpl::getDescriptor(Descriptor* _aidl_return) {
+ RETURN_IF(!_aidl_return, EX_ILLEGAL_ARGUMENT, "Parameter:nullptr");
+ LOG(DEBUG) << __func__ << kDescriptor.toString();
+ *_aidl_return = kDescriptor;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus HapticGeneratorImpl::commandImpl(CommandId command) {
+ RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+ switch (command) {
+ case CommandId::START:
+ mContext->enable();
+ break;
+ case CommandId::STOP:
+ mContext->disable();
+ break;
+ case CommandId::RESET:
+ mContext->reset();
+ break;
+ default:
+ LOG(ERROR) << __func__ << " commandId " << toString(command) << " not supported";
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "commandIdNotSupported");
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus HapticGeneratorImpl::setParameterSpecific(const Parameter::Specific& specific) {
+ RETURN_IF(Parameter::Specific::hapticGenerator != specific.getTag(), EX_ILLEGAL_ARGUMENT,
+ "EffectNotSupported");
+ RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+
+ auto& hgParam = specific.get<Parameter::Specific::hapticGenerator>();
+ auto tag = hgParam.getTag();
+
+ switch (tag) {
+ case HapticGenerator::hapticScales: {
+ RETURN_IF(mContext->setHgHapticScales(hgParam.get<HapticGenerator::hapticScales>()) !=
+ RetCode::SUCCESS,
+ EX_ILLEGAL_ARGUMENT, "setHapticScaleFailed");
+ return ndk::ScopedAStatus::ok();
+ }
+ case HapticGenerator::vibratorInfo: {
+ RETURN_IF(mContext->setHgVibratorInformation(
+ hgParam.get<HapticGenerator::vibratorInfo>()) != RetCode::SUCCESS,
+ EX_ILLEGAL_ARGUMENT, "setVibratorInfoFailed");
+ return ndk::ScopedAStatus::ok();
+ }
+ default: {
+ LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag);
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+ EX_ILLEGAL_ARGUMENT, "HapticGeneratorTagNotSupported");
+ }
+ }
+}
+
+ndk::ScopedAStatus HapticGeneratorImpl::getParameterSpecific(const Parameter::Id& id,
+ Parameter::Specific* specific) {
+ RETURN_IF(!specific, EX_NULL_POINTER, "nullPtr");
+ auto tag = id.getTag();
+ RETURN_IF(Parameter::Id::hapticGeneratorTag != tag, EX_ILLEGAL_ARGUMENT, "wrongIdTag");
+ auto hgId = id.get<Parameter::Id::hapticGeneratorTag>();
+ auto hgIdTag = hgId.getTag();
+ switch (hgIdTag) {
+ case HapticGenerator::Id::commonTag:
+ return getParameterHapticGenerator(hgId.get<HapticGenerator::Id::commonTag>(),
+ specific);
+ default:
+ LOG(ERROR) << __func__ << " unsupported tag: " << toString(hgIdTag);
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+ EX_ILLEGAL_ARGUMENT, "HapticGeneratorTagNotSupported");
+ }
+}
+
+ndk::ScopedAStatus HapticGeneratorImpl::getParameterHapticGenerator(const HapticGenerator::Tag& tag,
+ Parameter::Specific* specific) {
+ RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+
+ HapticGenerator hgParam;
+ switch (tag) {
+ case HapticGenerator::hapticScales: {
+ hgParam.set<HapticGenerator::hapticScales>(mContext->getHgHapticScales());
+ break;
+ }
+ case HapticGenerator::vibratorInfo: {
+ hgParam.set<HapticGenerator::vibratorInfo>(mContext->getHgVibratorInformation());
+ break;
+ }
+ default: {
+ LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag);
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+ EX_ILLEGAL_ARGUMENT, "HapticGeneratorTagNotSupported");
+ }
+ }
+
+ specific->set<Parameter::Specific::hapticGenerator>(hgParam);
+ return ndk::ScopedAStatus::ok();
+}
+
+std::shared_ptr<EffectContext> HapticGeneratorImpl::createContext(const Parameter::Common& common) {
+ if (mContext) {
+ LOG(DEBUG) << __func__ << " context already exist";
+ return mContext;
+ }
+
+ mContext = std::make_shared<HapticGeneratorContext>(1 /* statusFmqDepth */, common);
+ return mContext;
+}
+
+RetCode HapticGeneratorImpl::releaseContext() {
+ if (mContext) {
+ mContext->reset();
+ }
+ return RetCode::SUCCESS;
+}
+
+// Processing method running in EffectWorker thread.
+IEffect::Status HapticGeneratorImpl::effectProcessImpl(float* in, float* out, int samples) {
+ IEffect::Status status = {EX_NULL_POINTER, 0, 0};
+ RETURN_VALUE_IF(!mContext, status, "nullContext");
+ return mContext->lvmProcess(in, out, samples);
+}
+
+} // namespace aidl::android::hardware::audio::effect
diff --git a/media/libeffects/hapticgenerator/aidl/EffectHapticGenerator.h b/media/libeffects/hapticgenerator/aidl/EffectHapticGenerator.h
new file mode 100644
index 0000000..02ca392
--- /dev/null
+++ b/media/libeffects/hapticgenerator/aidl/EffectHapticGenerator.h
@@ -0,0 +1,55 @@
+/*
+ * 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/effect/BnEffect.h>
+
+#include "HapticGeneratorContext.h"
+#include "effect-impl/EffectImpl.h"
+#include "effect-impl/EffectUUID.h"
+
+namespace aidl::android::hardware::audio::effect {
+
+class HapticGeneratorImpl final : public EffectImpl {
+ public:
+ static const std::string kEffectName;
+ static const Descriptor kDescriptor;
+ HapticGeneratorImpl() { LOG(DEBUG) << __func__; }
+ ~HapticGeneratorImpl() {
+ cleanUp();
+ LOG(DEBUG) << __func__;
+ }
+
+ ndk::ScopedAStatus commandImpl(CommandId command) override;
+ ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
+ ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific) override;
+ ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id,
+ Parameter::Specific* specific) override;
+ IEffect::Status effectProcessImpl(float* in, float* out, int process) override;
+ std::shared_ptr<EffectContext> createContext(const Parameter::Common& common) override;
+ RetCode releaseContext() override;
+
+ std::shared_ptr<EffectContext> getContext() override { return mContext; }
+ std::string getEffectName() override { return kEffectName; }
+
+ private:
+ std::shared_ptr<HapticGeneratorContext> mContext;
+ ndk::ScopedAStatus getParameterHapticGenerator(const HapticGenerator::Tag& tag,
+ Parameter::Specific* specific);
+};
+
+} // namespace aidl::android::hardware::audio::effect
diff --git a/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.cpp b/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.cpp
new file mode 100644
index 0000000..64f51c3
--- /dev/null
+++ b/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.cpp
@@ -0,0 +1,346 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "AHAL_HapticGeneratorContext"
+
+#include <Utils.h>
+#include <android-base/parsedouble.h>
+#include <android-base/properties.h>
+
+#include "HapticGeneratorContext.h"
+
+namespace aidl::android::hardware::audio::effect {
+
+HapticGeneratorContext::HapticGeneratorContext(int statusDepth, const Parameter::Common& common)
+ : EffectContext(statusDepth, common) {
+ LOG(DEBUG) << __func__;
+ mState = HAPTIC_GENERATOR_STATE_UNINITIALIZED;
+ mSampleRate = common.input.base.sampleRate;
+ mFrameCount = common.input.frameCount;
+ init_params(common.input.base.channelMask, common.output.base.channelMask);
+}
+
+HapticGeneratorContext::~HapticGeneratorContext() {
+ LOG(DEBUG) << __func__;
+ mState = HAPTIC_GENERATOR_STATE_UNINITIALIZED;
+}
+
+RetCode HapticGeneratorContext::enable() {
+ if (mState != HAPTIC_GENERATOR_STATE_INITIALIZED) {
+ return RetCode::ERROR_EFFECT_LIB_ERROR;
+ }
+ mState = HAPTIC_GENERATOR_STATE_ACTIVE;
+ return RetCode::SUCCESS;
+}
+
+RetCode HapticGeneratorContext::disable() {
+ if (mState != HAPTIC_GENERATOR_STATE_ACTIVE) {
+ return RetCode::ERROR_EFFECT_LIB_ERROR;
+ }
+ mState = HAPTIC_GENERATOR_STATE_INITIALIZED;
+ return RetCode::SUCCESS;
+}
+
+void HapticGeneratorContext::reset() {
+ for (auto& filter : mProcessorsRecord.filters) {
+ filter->clear();
+ }
+ for (auto& slowEnv : mProcessorsRecord.slowEnvs) {
+ slowEnv->clear();
+ }
+ for (auto& distortion : mProcessorsRecord.distortions) {
+ distortion->clear();
+ }
+}
+
+RetCode HapticGeneratorContext::setHgHapticScales(
+ const std::vector<HapticGenerator::HapticScale> hapticScales) {
+ std::lock_guard lg(mMutex);
+ for (auto hapticScale : hapticScales) {
+ mParams.mHapticScales.insert_or_assign(hapticScale.id, hapticScale.scale);
+ }
+ mParams.mMaxVibratorScale = HapticGenerator::VibratorScale::MUTE;
+ for (const auto& [id, vibratorScale] : mParams.mHapticScales) {
+ mParams.mMaxVibratorScale = std::max(mParams.mMaxVibratorScale, vibratorScale);
+ }
+ return RetCode::SUCCESS;
+}
+
+HapticGenerator::VibratorInformation HapticGeneratorContext::getHgVibratorInformation() {
+ std::lock_guard lg(mMutex);
+ return mParams.mVibratorInfo;
+}
+
+std::vector<HapticGenerator::HapticScale> HapticGeneratorContext::getHgHapticScales() {
+ std::vector<HapticGenerator::HapticScale> result;
+ std::lock_guard lg(mMutex);
+ for (const auto& [id, vibratorScale] : mParams.mHapticScales) {
+ result.push_back({id, vibratorScale});
+ }
+ return result;
+}
+
+RetCode HapticGeneratorContext::setHgVibratorInformation(
+ const HapticGenerator::VibratorInformation& vibratorInfo) {
+ {
+ std::lock_guard lg(mMutex);
+ mParams.mVibratorInfo = vibratorInfo;
+
+ if (mProcessorsRecord.bpf != nullptr) {
+ mProcessorsRecord.bpf->setCoefficients(
+ ::android::audio_effect::haptic_generator::bpfCoefs(
+ mParams.mVibratorInfo.resonantFrequencyHz, DEFAULT_BPF_Q, mSampleRate));
+ }
+ if (mProcessorsRecord.bsf != nullptr) {
+ mProcessorsRecord.bsf->setCoefficients(
+ ::android::audio_effect::haptic_generator::bsfCoefs(
+ mParams.mVibratorInfo.resonantFrequencyHz,
+ mParams.mVibratorInfo.qFactor, mParams.mVibratorInfo.qFactor / 2.0f,
+ mSampleRate));
+ }
+ }
+ configure();
+ return RetCode::SUCCESS;
+}
+
+IEffect::Status HapticGeneratorContext::lvmProcess(float* in, float* out, int samples) {
+ LOG(DEBUG) << __func__ << " in " << in << " out " << out << " sample " << samples;
+
+ IEffect::Status status = {EX_NULL_POINTER, 0, 0};
+ RETURN_VALUE_IF(!in, status, "nullInput");
+ RETURN_VALUE_IF(!out, status, "nullOutput");
+ status = {EX_ILLEGAL_STATE, 0, 0};
+ RETURN_VALUE_IF(getInputFrameSize() != getOutputFrameSize(), status, "FrameSizeMismatch");
+ auto frameSize = getInputFrameSize();
+ RETURN_VALUE_IF(0 == frameSize, status, "zeroFrameSize");
+
+ LOG(DEBUG) << __func__ << " start processing";
+ // The audio data must not be modified but just written to
+ // output buffer according the access mode.
+ bool accumulate = false;
+ if (in != out) {
+ for (int i = 0; i < samples; i++) {
+ if (accumulate) {
+ out[i] += in[i];
+ } else {
+ out[i] = in[i];
+ }
+ }
+ }
+
+ if (mState != HAPTIC_GENERATOR_STATE_ACTIVE) {
+ return status;
+ }
+
+ std::lock_guard lg(mMutex);
+ if (mParams.mMaxVibratorScale == HapticGenerator::VibratorScale::MUTE) {
+ // Haptic channels are muted, not need to generate haptic data.
+ return {STATUS_OK, samples, samples};
+ }
+
+ // Resize buffer if the haptic sample count is greater than buffer size.
+ size_t hapticSampleCount = mFrameCount * mParams.mHapticChannelCount;
+ if (hapticSampleCount > mInputBuffer.size()) {
+ // The inputBuffer and outputBuffer must have the same size, which must be at least
+ // the haptic sample count.
+ mInputBuffer.resize(hapticSampleCount);
+ mOutputBuffer.resize(hapticSampleCount);
+ }
+
+ // Construct input buffer according to haptic channel source
+ for (size_t i = 0; i < mFrameCount; ++i) {
+ for (size_t j = 0; j < mParams.mHapticChannelCount; ++j) {
+ mInputBuffer[i * mParams.mHapticChannelCount + j] =
+ in[i * mParams.mAudioChannelCount + mParams.mHapticChannelSource[j]];
+ }
+ }
+
+ float* hapticOutBuffer =
+ runProcessingChain(mInputBuffer.data(), mOutputBuffer.data(), mFrameCount);
+ ::android::os::scaleHapticData(
+ hapticOutBuffer, hapticSampleCount,
+ static_cast<::android::os::HapticScale>(mParams.mMaxVibratorScale),
+ mParams.mVibratorInfo.qFactor);
+
+ // For haptic data, the haptic playback thread will copy the data from effect input
+ // buffer, which contains haptic data at the end of the buffer, directly to sink buffer.
+ // In that case, copy haptic data to input buffer instead of output buffer.
+ // Note: this may not work with rpc/binder calls
+ int offset = samples;
+ for (int i = 0; i < hapticSampleCount; ++i) {
+ in[samples + i] = hapticOutBuffer[i];
+ }
+ return {STATUS_OK, samples, static_cast<int32_t>(samples + hapticSampleCount)};
+}
+
+void HapticGeneratorContext::init_params(media::audio::common::AudioChannelLayout inputChMask,
+ media::audio::common::AudioChannelLayout outputChMask) {
+ std::lock_guard lg(mMutex);
+ mParams.mMaxVibratorScale = HapticGenerator::VibratorScale::MUTE;
+ mParams.mVibratorInfo.resonantFrequencyHz = DEFAULT_RESONANT_FREQUENCY;
+ mParams.mVibratorInfo.qFactor = DEFAULT_BSF_ZERO_Q;
+
+ mParams.mAudioChannelCount = ::android::hardware::audio::common::getChannelCount(
+ inputChMask, ~media::audio::common::AudioChannelLayout::LAYOUT_HAPTIC_AB);
+ mParams.mHapticChannelCount = ::android::hardware::audio::common::getChannelCount(
+ outputChMask, media::audio::common::AudioChannelLayout::LAYOUT_HAPTIC_AB);
+ LOG_ALWAYS_FATAL_IF(mParams.mHapticChannelCount > 2, "haptic channel count is too large");
+ for (size_t i = 0; i < mParams.mHapticChannelCount; ++i) {
+ // By default, use the first audio channel to generate haptic channels.
+ mParams.mHapticChannelSource[i] = 0;
+ }
+
+ mState = HAPTIC_GENERATOR_STATE_INITIALIZED;
+}
+
+float HapticGeneratorContext::getDistortionOutputGain() {
+ float distortionOutputGain = getFloatProperty(
+ "vendor.audio.hapticgenerator.distortion.output.gain", DEFAULT_DISTORTION_OUTPUT_GAIN);
+ LOG(DEBUG) << "Using distortion output gain as " << distortionOutputGain;
+ return distortionOutputGain;
+}
+
+float HapticGeneratorContext::getFloatProperty(const std::string& key, float defaultValue) {
+ float result;
+ std::string value = ::android::base::GetProperty(key, "");
+ if (!value.empty() && ::android::base::ParseFloat(value, &result)) {
+ return result;
+ }
+ return defaultValue;
+}
+
+void HapticGeneratorContext::addBiquadFilter(std::shared_ptr<HapticBiquadFilter> filter) {
+ // The process chain captures the shared pointer of the filter in lambda.
+ // The process record will keep a shared pointer to the filter so that it is possible to
+ // access the filter outside of the process chain.
+ mProcessorsRecord.filters.push_back(filter);
+ mProcessingChain.push_back([filter](float* out, const float* in, size_t frameCount) {
+ filter->process(out, in, frameCount);
+ });
+}
+
+/**
+ * Build haptic generator processing chain.
+ */
+void HapticGeneratorContext::buildProcessingChain() {
+ std::lock_guard lg(mMutex);
+ const size_t channelCount = mParams.mHapticChannelCount;
+ float highPassCornerFrequency = 50.0f;
+ auto hpf = ::android::audio_effect::haptic_generator::createHPF2(highPassCornerFrequency,
+ mSampleRate, channelCount);
+ addBiquadFilter(hpf);
+ float lowPassCornerFrequency = 9000.0f;
+ auto lpf = ::android::audio_effect::haptic_generator::createLPF2(lowPassCornerFrequency,
+ mSampleRate, channelCount);
+ addBiquadFilter(lpf);
+
+ auto ramp = std::make_shared<::android::audio_effect::haptic_generator::Ramp>(
+ channelCount); // ramp = half-wave rectifier.
+ // The process chain captures the shared pointer of the ramp in lambda. It will be the only
+ // reference to the ramp.
+ // The process record will keep a weak pointer to the ramp so that it is possible to access
+ // the ramp outside of the process chain.
+ mProcessorsRecord.ramps.push_back(ramp);
+ mProcessingChain.push_back([ramp](float* out, const float* in, size_t frameCount) {
+ ramp->process(out, in, frameCount);
+ });
+
+ highPassCornerFrequency = 60.0f;
+ hpf = ::android::audio_effect::haptic_generator::createHPF2(highPassCornerFrequency,
+ mSampleRate, channelCount);
+ addBiquadFilter(hpf);
+ lowPassCornerFrequency = 700.0f;
+ lpf = ::android::audio_effect::haptic_generator::createLPF2(lowPassCornerFrequency, mSampleRate,
+ channelCount);
+ addBiquadFilter(lpf);
+
+ lowPassCornerFrequency = 400.0f;
+ lpf = ::android::audio_effect::haptic_generator::createLPF2(lowPassCornerFrequency, mSampleRate,
+ channelCount);
+ addBiquadFilter(lpf);
+ lowPassCornerFrequency = 500.0f;
+ lpf = ::android::audio_effect::haptic_generator::createLPF2(lowPassCornerFrequency, mSampleRate,
+ channelCount);
+ addBiquadFilter(lpf);
+
+ auto bpf = ::android::audio_effect::haptic_generator::createBPF(
+ mParams.mVibratorInfo.resonantFrequencyHz, DEFAULT_BPF_Q, mSampleRate, channelCount);
+ mProcessorsRecord.bpf = bpf;
+ addBiquadFilter(bpf);
+
+ float normalizationPower = DEFAULT_SLOW_ENV_NORMALIZATION_POWER;
+ // The process chain captures the shared pointer of the slow envelope in lambda. It will
+ // be the only reference to the slow envelope.
+ // The process record will keep a weak pointer to the slow envelope so that it is possible
+ // to access the slow envelope outside of the process chain.
+ // SlowEnvelope = partial normalizer, or AGC.
+ auto slowEnv = std::make_shared<::android::audio_effect::haptic_generator::SlowEnvelope>(
+ 5.0f /*envCornerFrequency*/, mSampleRate, normalizationPower, 0.01f /*envOffset*/,
+ channelCount);
+ mProcessorsRecord.slowEnvs.push_back(slowEnv);
+ mProcessingChain.push_back([slowEnv](float* out, const float* in, size_t frameCount) {
+ slowEnv->process(out, in, frameCount);
+ });
+
+ auto bsf = ::android::audio_effect::haptic_generator::createBSF(
+ mParams.mVibratorInfo.resonantFrequencyHz, mParams.mVibratorInfo.qFactor,
+ mParams.mVibratorInfo.qFactor / 2.0f, mSampleRate, channelCount);
+ mProcessorsRecord.bsf = bsf;
+ addBiquadFilter(bsf);
+
+ // The process chain captures the shared pointer of the Distortion in lambda. It will
+ // be the only reference to the Distortion.
+ // The process record will keep a weak pointer to the Distortion so that it is possible
+ // to access the Distortion outside of the process chain.
+ auto distortion = std::make_shared<::android::audio_effect::haptic_generator::Distortion>(
+ DEFAULT_DISTORTION_CORNER_FREQUENCY, mSampleRate, DEFAULT_DISTORTION_INPUT_GAIN,
+ DEFAULT_DISTORTION_CUBE_THRESHOLD, getDistortionOutputGain(), channelCount);
+ mProcessorsRecord.distortions.push_back(distortion);
+ mProcessingChain.push_back([distortion](float* out, const float* in, size_t frameCount) {
+ distortion->process(out, in, frameCount);
+ });
+}
+
+void HapticGeneratorContext::configure() {
+ mProcessingChain.clear();
+ mProcessorsRecord.filters.clear();
+ mProcessorsRecord.ramps.clear();
+ mProcessorsRecord.slowEnvs.clear();
+ mProcessorsRecord.distortions.clear();
+
+ buildProcessingChain();
+}
+
+/**
+ * Run the processing chain to generate haptic data from audio data
+ *
+ * @param buf1 a buffer contains raw audio data
+ * @param buf2 a buffer that is large enough to keep all the data
+ * @param frameCount frame count of the data
+ *
+ * @return a pointer to the output buffer
+ */
+float* HapticGeneratorContext::runProcessingChain(float* buf1, float* buf2, size_t frameCount) {
+ float* in = buf1;
+ float* out = buf2;
+ for (const auto processingFunc : mProcessingChain) {
+ processingFunc(out, in, frameCount);
+ std::swap(in, out);
+ }
+ return in;
+}
+
+} // namespace aidl::android::hardware::audio::effect
diff --git a/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.h b/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.h
new file mode 100644
index 0000000..dc43feb
--- /dev/null
+++ b/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.h
@@ -0,0 +1,124 @@
+/*
+ * 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-base/thread_annotations.h>
+#include <vibrator/ExternalVibrationUtils.h>
+#include <map>
+
+#include "Processors.h"
+#include "effect-impl/EffectContext.h"
+
+namespace aidl::android::hardware::audio::effect {
+
+enum HapticGeneratorState {
+ HAPTIC_GENERATOR_STATE_UNINITIALIZED,
+ HAPTIC_GENERATOR_STATE_INITIALIZED,
+ HAPTIC_GENERATOR_STATE_ACTIVE,
+};
+
+struct HapticGeneratorParam {
+ // The audio channels used to generate haptic channels. The first channel will be used to
+ // generate HAPTIC_A, The second channel will be used to generate HAPTIC_B.
+ // The value will be offset of audio channel
+ int mHapticChannelSource[2];
+
+ int mHapticChannelCount;
+ int mAudioChannelCount;
+
+ HapticGenerator::HapticScale mHapticScale;
+ std::map<int, HapticGenerator::VibratorScale> mHapticScales;
+ // max intensity will be used to scale haptic data.
+ HapticGenerator::VibratorScale mMaxVibratorScale;
+
+ HapticGenerator::VibratorInformation mVibratorInfo;
+};
+
+// A structure to keep all shared pointers for all processors in HapticGenerator.
+struct HapticGeneratorProcessorsRecord {
+ std::vector<std::shared_ptr<HapticBiquadFilter>> filters;
+ std::vector<std::shared_ptr<::android::audio_effect::haptic_generator::Ramp>> ramps;
+ std::vector<std::shared_ptr<::android::audio_effect::haptic_generator::SlowEnvelope>> slowEnvs;
+ std::vector<std::shared_ptr<::android::audio_effect::haptic_generator::Distortion>> distortions;
+
+ // Cache band-pass filter and band-stop filter for updating parameters
+ // according to vibrator info
+ std::shared_ptr<HapticBiquadFilter> bpf;
+ std::shared_ptr<HapticBiquadFilter> bsf;
+};
+
+class HapticGeneratorContext final : public EffectContext {
+ public:
+ HapticGeneratorContext(int statusDepth, const Parameter::Common& common);
+ ~HapticGeneratorContext();
+ RetCode enable();
+ RetCode disable();
+ void reset();
+
+ RetCode setHgHapticScales(const std::vector<HapticGenerator::HapticScale> hapticScales);
+ std::vector<HapticGenerator::HapticScale> getHgHapticScales();
+
+ RetCode setHgVibratorInformation(const HapticGenerator::VibratorInformation& vibratorInfo);
+ HapticGenerator::VibratorInformation getHgVibratorInformation();
+
+ IEffect::Status lvmProcess(float* in, float* out, int samples);
+
+ private:
+ static constexpr float DEFAULT_RESONANT_FREQUENCY = 150.0f;
+ static constexpr float DEFAULT_BSF_ZERO_Q = 8.0f;
+ static constexpr float DEFAULT_BSF_POLE_Q = 4.0f;
+ static constexpr float DEFAULT_DISTORTION_OUTPUT_GAIN = 1.5f;
+ static constexpr float DEFAULT_BPF_Q = 1.0f;
+ static constexpr float DEFAULT_SLOW_ENV_NORMALIZATION_POWER = -0.8f;
+ static constexpr float DEFAULT_DISTORTION_CORNER_FREQUENCY = 300.0f;
+ static constexpr float DEFAULT_DISTORTION_INPUT_GAIN = 0.3f;
+ static constexpr float DEFAULT_DISTORTION_CUBE_THRESHOLD = 0.1f;
+
+ std::mutex mMutex;
+ HapticGeneratorState mState;
+ HapticGeneratorParam mParams GUARDED_BY(mMutex);
+ int mSampleRate;
+ int mFrameCount = 0;
+
+ // A cache for all shared pointers of the HapticGenerator
+ struct HapticGeneratorProcessorsRecord mProcessorsRecord;
+
+ // Using a vector of functions to record the processing chain for haptic-generating algorithm.
+ // The three parameters of the processing functions are pointer to output buffer, pointer to
+ // input buffer and frame count.
+ std::vector<std::function<void(float*, const float*, size_t)>> mProcessingChain;
+
+ // inputBuffer is where to keep input buffer for the generating algorithm. It will be
+ // constructed according to hapticChannelSource.
+ std::vector<float> mInputBuffer;
+
+ // outputBuffer is a buffer having the same length as inputBuffer. It can be used as
+ // intermediate buffer in the generating algorithm.
+ std::vector<float> mOutputBuffer;
+
+ void init_params(media::audio::common::AudioChannelLayout inputChMask,
+ media::audio::common::AudioChannelLayout outputChMask);
+ void configure();
+
+ float getDistortionOutputGain();
+ float getFloatProperty(const std::string& key, float defaultValue);
+ void addBiquadFilter(std::shared_ptr<HapticBiquadFilter> filter);
+ void buildProcessingChain();
+ float* runProcessingChain(float* buf1, float* buf2, size_t frameCount);
+};
+
+} // namespace aidl::android::hardware::audio::effect
diff --git a/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp b/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp
index 1bcde74..3aee721 100644
--- a/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp
+++ b/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp
@@ -96,8 +96,11 @@
mSamplesToExitCountVirt = (mSamplesPerSecond * 0.1);
tempDisabled = mVirtualizerTempDisabled;
break;
- default:
- // Add handling for other effects
+ case lvm::BundleEffectType::VOLUME:
+ LOG(DEBUG) << __func__ << " enable bundle VOL";
+ if ((mEffectInDrain & (1 << int(lvm::BundleEffectType::VOLUME))) == 0)
+ mNumberEffectsEnabled++;
+ mEffectInDrain &= ~(1 << int(lvm::BundleEffectType::VOLUME));
break;
}
mEnabled = true;
@@ -123,8 +126,8 @@
LOG(DEBUG) << __func__ << " enable bundle VR";
params.VirtualizerOperatingMode = LVM_MODE_ON;
break;
- default:
- // Add handling for other effects
+ case lvm::BundleEffectType::VOLUME:
+ LOG(DEBUG) << __func__ << " enable bundle VOL";
break;
}
RETURN_VALUE_IF(LVM_SUCCESS != LVM_SetControlParameters(mInstance, ¶ms),
@@ -148,8 +151,9 @@
LOG(DEBUG) << __func__ << " disable bundle VR";
mEffectInDrain |= 1 << int(lvm::BundleEffectType::VIRTUALIZER);
break;
- default:
- // Add handling for other effects
+ case lvm::BundleEffectType::VOLUME:
+ LOG(DEBUG) << __func__ << " disable bundle VOL";
+ mEffectInDrain |= 1 << int(lvm::BundleEffectType::VOLUME);
break;
}
mEnabled = false;
@@ -175,8 +179,8 @@
LOG(DEBUG) << __func__ << " disable bundle VR";
params.VirtualizerOperatingMode = LVM_MODE_OFF;
break;
- default:
- // Add handling for other effects
+ case lvm::BundleEffectType::VOLUME:
+ LOG(DEBUG) << __func__ << " disable bundle VOL";
break;
}
RETURN_VALUE_IF(LVM_SUCCESS != LVM_SetControlParameters(mInstance, ¶ms),
@@ -255,15 +259,15 @@
// roundoff
int maxLevelRound = (int)(totalEnergyEstimation + 0.99);
- if (maxLevelRound + mLevelSaved > 0) {
- gainCorrection = maxLevelRound + mLevelSaved;
+ if (maxLevelRound + mVolume > 0) {
+ gainCorrection = maxLevelRound + mVolume;
}
- params.VC_EffectLevel = mLevelSaved - gainCorrection;
+ params.VC_EffectLevel = mVolume - gainCorrection;
if (params.VC_EffectLevel < -96) {
params.VC_EffectLevel = -96;
}
- LOG(INFO) << "\tVol: " << mLevelSaved << ", GainCorrection: " << gainCorrection
+ LOG(INFO) << "\tVol: " << mVolume << ", GainCorrection: " << gainCorrection
<< ", Actual vol: " << params.VC_EffectLevel;
/* Activate the initial settings */
@@ -395,8 +399,7 @@
int rightdB = VolToDb(volume.right);
int maxdB = std::max(leftdB, rightdB);
int pandB = rightdB - leftdB;
- // TODO: add volume effect implementation here:
- // android::VolumeSetVolumeLevel(pContext, (int16_t)(maxdB * 100));
+ setVolumeLevel(maxdB * 100);
LOG(DEBUG) << __func__ << " pandB: " << pandB << " maxdB " << maxdB;
{
@@ -495,8 +498,7 @@
}
RetCode BundleContext::setBassBoostStrength(int strength) {
- if (strength < BassBoost::MIN_PER_MILLE_STRENGTH ||
- strength > BassBoost::MAX_PER_MILLE_STRENGTH) {
+ if (strength < 0 || strength > lvm::kBassBoostCap.maxStrengthPm) {
LOG(ERROR) << __func__ << " invalid strength: " << strength;
return RetCode::ERROR_ILLEGAL_PARAMETER;
}
@@ -519,9 +521,37 @@
return limitLevel();
}
+RetCode BundleContext::setVolumeLevel(int level) {
+ if (level < lvm::kVolumeCap.minLevelDb || level > lvm::kVolumeCap.maxLevelDb) {
+ return RetCode::ERROR_ILLEGAL_PARAMETER;
+ }
+
+ if (mMuteEnabled) {
+ mLevelSaved = level / 100;
+ } else {
+ mVolume = level / 100;
+ }
+ LOG(INFO) << __func__ << " success with level " << level;
+ return limitLevel();
+}
+
+int BundleContext::getVolumeLevel() const {
+ return (mMuteEnabled ? mLevelSaved * 100 : mVolume * 100);
+}
+
+RetCode BundleContext::setVolumeMute(bool mute) {
+ mMuteEnabled = mute;
+ if (mMuteEnabled) {
+ mLevelSaved = mVolume;
+ mVolume = -96;
+ } else {
+ mVolume = mLevelSaved;
+ }
+ return limitLevel();
+}
+
RetCode BundleContext::setVirtualizerStrength(int strength) {
- if (strength < Virtualizer::MIN_PER_MILLE_STRENGTH ||
- strength > Virtualizer::MAX_PER_MILLE_STRENGTH) {
+ if (strength < 0 || strength > lvm::kVirtualizerCap.maxStrengthPm) {
return RetCode::ERROR_ILLEGAL_PARAMETER;
}
@@ -662,6 +692,11 @@
--mNumberEffectsEnabled;
mEffectInDrain &= ~(1 << int(lvm::BundleEffectType::VIRTUALIZER));
}
+ if ((undrainedEffects & 1 << int(lvm::BundleEffectType::VOLUME)) != 0) {
+ LOG(DEBUG) << "Draining VOLUME";
+ --mNumberEffectsEnabled;
+ mEffectInDrain &= ~(1 << int(lvm::BundleEffectType::VOLUME));
+ }
}
mEffectProcessCalled |= 1 << int(mType);
if (!mEnabled) {
@@ -705,8 +740,13 @@
LOG(DEBUG) << "Effect_process() this is the last frame for VIRTUALIZER";
}
break;
- default:
- // Add handling for other effects
+ case lvm::BundleEffectType::VOLUME:
+ isDataAvailable = false;
+ if ((mEffectInDrain & 1 << int(lvm::BundleEffectType::VOLUME)) != 0) {
+ mNumberEffectsEnabled--;
+ mEffectInDrain &= ~(1 << int(lvm::BundleEffectType::VOLUME));
+ }
+ LOG(DEBUG) << "Effect_process() LVM_VOLUME Effect is not enabled";
break;
}
}
diff --git a/media/libeffects/lvm/wrapper/Aidl/BundleContext.h b/media/libeffects/lvm/wrapper/Aidl/BundleContext.h
index 116ae6f..be723f7 100644
--- a/media/libeffects/lvm/wrapper/Aidl/BundleContext.h
+++ b/media/libeffects/lvm/wrapper/Aidl/BundleContext.h
@@ -71,6 +71,12 @@
RetCode setBassBoostStrength(int strength);
int getBassBoostStrength() const { return mBassStrengthSaved; }
+ RetCode setVolumeLevel(int level);
+ int getVolumeLevel() const;
+
+ RetCode setVolumeMute(bool mute);
+ int getVolumeMute() const { return mMuteEnabled; }
+
RetCode setVirtualizerStrength(int strength);
int getVirtualizerStrength() const { return mVirtStrengthSaved; }
@@ -106,7 +112,7 @@
int mEffectProcessCalled = 0;
int mNumberEffectsEnabled = 0;
int mNumberEffectsCalled = 0;
- bool mFirstVolume = false;
+ bool mFirstVolume = true;
// Bass
bool mBassTempDisabled = false;
int mBassStrengthSaved = 0;
@@ -118,6 +124,7 @@
bool mVirtualizerTempDisabled = false;
// Volume
int mLevelSaved = 0; /* for when mute is set, level must be saved */
+ int mVolume = 0;
bool mMuteEnabled = false; /* Must store as mute = -96dB level */
void initControlParameter(LVM_ControlParams_t& params) const;
diff --git a/media/libeffects/lvm/wrapper/Aidl/BundleTypes.h b/media/libeffects/lvm/wrapper/Aidl/BundleTypes.h
index 95a0dab..1996240 100644
--- a/media/libeffects/lvm/wrapper/Aidl/BundleTypes.h
+++ b/media/libeffects/lvm/wrapper/Aidl/BundleTypes.h
@@ -85,7 +85,8 @@
static const bool mStrengthSupported = true;
-static const BassBoost::Capability kBassBoostCap = {.strengthSupported = mStrengthSupported};
+static const BassBoost::Capability kBassBoostCap = {.maxStrengthPm = 1000,
+ .strengthSupported = mStrengthSupported};
static const std::string kBassBoostEffectName = "Dynamic Bass Boost";
@@ -103,7 +104,8 @@
.implementor = "NXP Software Ltd."},
.capability = Capability::make<Capability::bassBoost>(kBassBoostCap)};
-static const Virtualizer::Capability kVirtualizerCap = {.strengthSupported = mStrengthSupported};
+static const Virtualizer::Capability kVirtualizerCap = {.maxStrengthPm = 1000,
+ .strengthSupported = mStrengthSupported};
static const std::string kVirtualizerEffectName = "Virtualizer";
@@ -112,7 +114,7 @@
.uuid = kVirtualizerBundleImplUUID,
.proxy = kVirtualizerProxyUUID},
.flags = {.type = Flags::Type::INSERT,
- .insert = Flags::Insert::FIRST,
+ .insert = Flags::Insert::LAST,
.volume = Flags::Volume::CTRL,
.deviceIndication = true},
.cpuLoad = VIRTUALIZER_CUP_LOAD_ARM9E,
@@ -121,8 +123,22 @@
.implementor = "NXP Software Ltd."},
.capability = Capability::make<Capability::virtualizer>(kVirtualizerCap)};
-// TODO: add descriptors for other bundle effect types here.
-static const Descriptor kVolumeDesc;
+static const Volume::Capability kVolumeCap = {.minLevelDb = -9600, .maxLevelDb = 0};
+
+static const std::string kVolumeEffectName = "Volume";
+
+static const Descriptor kVolumeDesc = {
+ .common = {.id = {.type = kVolumeTypeUUID,
+ .uuid = kVolumeBundleImplUUID,
+ .proxy = std::nullopt},
+ .flags = {.type = Flags::Type::INSERT,
+ .insert = Flags::Insert::LAST,
+ .volume = Flags::Volume::CTRL},
+ .cpuLoad = VOLUME_CUP_LOAD_ARM9E,
+ .memoryUsage = BUNDLE_MEM_USAGE,
+ .name = kVolumeEffectName,
+ .implementor = "NXP Software Ltd."},
+ .capability = Capability::make<Capability::volume>(kVolumeCap)};
/* The following tables have been computed using the actual levels measured by the output of
* white noise or pink noise (IEC268-1) for the EQ and BassBoost Effects. These are estimates of
diff --git a/media/libeffects/lvm/wrapper/Aidl/EffectBundleAidl.cpp b/media/libeffects/lvm/wrapper/Aidl/EffectBundleAidl.cpp
index b435ab4..81b8aca 100644
--- a/media/libeffects/lvm/wrapper/Aidl/EffectBundleAidl.cpp
+++ b/media/libeffects/lvm/wrapper/Aidl/EffectBundleAidl.cpp
@@ -35,12 +35,13 @@
using aidl::android::hardware::audio::effect::kBassBoostBundleImplUUID;
using aidl::android::hardware::audio::effect::kEqualizerBundleImplUUID;
using aidl::android::hardware::audio::effect::kVirtualizerBundleImplUUID;
+using aidl::android::hardware::audio::effect::kVolumeBundleImplUUID;
using aidl::android::hardware::audio::effect::State;
using aidl::android::media::audio::common::AudioUuid;
bool isUuidSupported(const AudioUuid* uuid) {
return (*uuid == kEqualizerBundleImplUUID || *uuid == kBassBoostBundleImplUUID ||
- *uuid == kVirtualizerBundleImplUUID);
+ *uuid == kVirtualizerBundleImplUUID || *uuid == kVolumeBundleImplUUID);
}
extern "C" binder_exception_t createEffect(const AudioUuid* uuid,
@@ -70,6 +71,8 @@
*_aidl_return = aidl::android::hardware::audio::effect::lvm:: kBassBoostDesc;
} else if (*in_impl_uuid == kVirtualizerBundleImplUUID) {
*_aidl_return = aidl::android::hardware::audio::effect::lvm::kVirtualizerDesc;
+ } else if (*in_impl_uuid == kVolumeBundleImplUUID) {
+ *_aidl_return = aidl::android::hardware::audio::effect::lvm::kVolumeDesc;
}
return EX_NONE;
}
@@ -90,9 +93,12 @@
mType = lvm::BundleEffectType::VIRTUALIZER;
mDescriptor = &lvm::kVirtualizerDesc;
mEffectName = &lvm::kVirtualizerEffectName;
+ } else if (uuid == kVolumeBundleImplUUID) {
+ mType = lvm::BundleEffectType::VOLUME;
+ mDescriptor = &lvm::kVolumeDesc;
+ mEffectName = &lvm::kVolumeEffectName;
} else {
- // TODO: add other bundle effect types here.
- LOG(ERROR) << __func__ << uuid.toString() << " not supported yet!";
+ LOG(ERROR) << __func__ << uuid.toString() << " not supported!";
}
}
@@ -156,6 +162,8 @@
return setParameterBassBoost(specific);
case Parameter::Specific::virtualizer:
return setParameterVirtualizer(specific);
+ case Parameter::Specific::volume:
+ return setParameterVolume(specific);
default:
LOG(ERROR) << __func__ << " unsupported tag " << toString(tag);
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
@@ -217,6 +225,26 @@
}
}
+ndk::ScopedAStatus EffectBundleAidl::setParameterVolume(const Parameter::Specific& specific) {
+ auto& vol = specific.get<Parameter::Specific::volume>();
+ auto volTag = vol.getTag();
+ switch (volTag) {
+ case Volume::levelDb: {
+ RETURN_IF(mContext->setVolumeLevel(vol.get<Volume::levelDb>()) != RetCode::SUCCESS,
+ EX_ILLEGAL_ARGUMENT, "setLevelFailed");
+ return ndk::ScopedAStatus::ok();
+ }
+ case Volume::mute:
+ RETURN_IF(mContext->setVolumeMute(vol.get<Volume::mute>()) != RetCode::SUCCESS,
+ EX_ILLEGAL_ARGUMENT, "setMuteFailed");
+ return ndk::ScopedAStatus::ok();
+ default:
+ LOG(ERROR) << __func__ << " unsupported parameter " << specific.toString();
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "volTagNotSupported");
+ }
+}
+
ndk::ScopedAStatus EffectBundleAidl::getParameterSpecific(const Parameter::Id& id,
Parameter::Specific* specific) {
RETURN_IF(!specific, EX_NULL_POINTER, "nullPtr");
@@ -229,6 +257,8 @@
return getParameterBassBoost(id.get<Parameter::Id::bassBoostTag>(), specific);
case Parameter::Id::virtualizerTag:
return getParameterVirtualizer(id.get<Parameter::Id::virtualizerTag>(), specific);
+ case Parameter::Id::volumeTag:
+ return getParameterVolume(id.get<Parameter::Id::volumeTag>(), specific);
default:
LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag);
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
@@ -288,6 +318,34 @@
return ndk::ScopedAStatus::ok();
}
+ndk::ScopedAStatus EffectBundleAidl::getParameterVolume(const Volume::Id& id,
+ Parameter::Specific* specific) {
+ RETURN_IF(id.getTag() != Volume::Id::commonTag, EX_ILLEGAL_ARGUMENT, "VolumeTagNotSupported");
+
+ RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+ Volume volParam;
+
+ auto tag = id.get<Volume::Id::commonTag>();
+ switch (tag) {
+ case Volume::levelDb: {
+ volParam.set<Volume::levelDb>(mContext->getVolumeLevel());
+ break;
+ }
+ case Volume::mute: {
+ volParam.set<Volume::mute>(mContext->getVolumeMute());
+ break;
+ }
+ default: {
+ LOG(ERROR) << __func__ << " not handled tag: " << toString(tag);
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "VolumeTagNotSupported");
+ }
+ }
+
+ specific->set<Parameter::Specific::volume>(volParam);
+ return ndk::ScopedAStatus::ok();
+}
+
ndk::ScopedAStatus EffectBundleAidl::getParameterVirtualizer(const Virtualizer::Id& id,
Parameter::Specific* specific) {
RETURN_IF(id.getTag() != Virtualizer::Id::commonTag, EX_ILLEGAL_ARGUMENT,
diff --git a/media/libeffects/lvm/wrapper/Aidl/EffectBundleAidl.h b/media/libeffects/lvm/wrapper/Aidl/EffectBundleAidl.h
index ebf100a..0330e5a 100644
--- a/media/libeffects/lvm/wrapper/Aidl/EffectBundleAidl.h
+++ b/media/libeffects/lvm/wrapper/Aidl/EffectBundleAidl.h
@@ -67,6 +67,8 @@
ndk::ScopedAStatus setParameterEqualizer(const Parameter::Specific& specific);
ndk::ScopedAStatus getParameterEqualizer(const Equalizer::Id& id,
Parameter::Specific* specific);
+ ndk::ScopedAStatus setParameterVolume(const Parameter::Specific& specific);
+ ndk::ScopedAStatus getParameterVolume(const Volume::Id& id, Parameter::Specific* specific);
ndk::ScopedAStatus setParameterVirtualizer(const Parameter::Specific& specific);
ndk::ScopedAStatus getParameterVirtualizer(const Virtualizer::Id& id,
Parameter::Specific* specific);
diff --git a/media/libeffects/preprocessing/PreProcessing.cpp b/media/libeffects/preprocessing/PreProcessing.cpp
index 61a2bf5..59f1fc3 100644
--- a/media/libeffects/preprocessing/PreProcessing.cpp
+++ b/media/libeffects/preprocessing/PreProcessing.cpp
@@ -102,7 +102,8 @@
uint32_t state; // current state (enum preproc_session_state)
int id; // audio session ID
int io; // handle of input stream this session is on
- webrtc::AudioProcessing* apm; // handle on webRTC audio processing module (APM)
+ rtc::scoped_refptr<webrtc::AudioProcessing>
+ apm; // handle on webRTC audio processing module (APM)
// Audio Processing module builder
webrtc::AudioProcessingBuilder ap_builder;
// frameCount represents the size of the buffers used for processing, and must represent 10ms.
@@ -260,9 +261,6 @@
ALOGV("Agc2Init");
effect->session->config = effect->session->apm->GetConfig();
effect->session->config.gain_controller2.fixed_digital.gain_db = 0.f;
- effect->session->config.gain_controller2.adaptive_digital.level_estimator =
- effect->session->config.gain_controller2.kRms;
- effect->session->config.gain_controller2.adaptive_digital.extra_saturation_margin_db = 2.f;
effect->session->apm->ApplyConfig(effect->session->config);
return 0;
}
@@ -332,24 +330,19 @@
ALOGV("Agc2GetParameter() target level %f dB", *(float*)pValue);
break;
case AGC2_PARAM_ADAPT_DIGI_LEVEL_ESTIMATOR:
- *(uint32_t*)pValue = (uint32_t)(
- effect->session->config.gain_controller2.adaptive_digital.level_estimator);
- ALOGV("Agc2GetParameter() level estimator %d",
- *(webrtc::AudioProcessing::Config::GainController2::LevelEstimator*)pValue);
+ // WebRTC only supports RMS level estimator now
+ *(uint32_t*)pValue = (uint32_t)(0);
+ ALOGV("Agc2GetParameter() level estimator RMS");
break;
case AGC2_PARAM_ADAPT_DIGI_EXTRA_SATURATION_MARGIN:
- *(float*)pValue = (float)(effect->session->config.gain_controller2.adaptive_digital
- .extra_saturation_margin_db);
+ *(float*)pValue = (float)(2.0);
ALOGV("Agc2GetParameter() extra saturation margin %f dB", *(float*)pValue);
break;
case AGC2_PARAM_PROPERTIES:
pProperties->fixedDigitalGain =
(float)(effect->session->config.gain_controller2.fixed_digital.gain_db);
- pProperties->level_estimator = (uint32_t)(
- effect->session->config.gain_controller2.adaptive_digital.level_estimator);
- pProperties->extraSaturationMargin =
- (float)(effect->session->config.gain_controller2.adaptive_digital
- .extra_saturation_margin_db);
+ pProperties->level_estimator = 0;
+ pProperties->extraSaturationMargin = 2.0;
break;
default:
ALOGW("Agc2GetParameter() unknown param %d", param);
@@ -438,16 +431,19 @@
effect->session->config.gain_controller2.fixed_digital.gain_db = valueFloat;
break;
case AGC2_PARAM_ADAPT_DIGI_LEVEL_ESTIMATOR:
- ALOGV("Agc2SetParameter() level estimator %d",
- *(webrtc::AudioProcessing::Config::GainController2::LevelEstimator*)pValue);
- effect->session->config.gain_controller2.adaptive_digital.level_estimator =
- (*(webrtc::AudioProcessing::Config::GainController2::LevelEstimator*)pValue);
+ ALOGV("Agc2SetParameter() level estimator %d", *(uint32_t*)pValue);
+ if (*(uint32_t*)pValue != 0) {
+ // only RMS is supported
+ status = -EINVAL;
+ }
break;
case AGC2_PARAM_ADAPT_DIGI_EXTRA_SATURATION_MARGIN:
valueFloat = (float)(*(int32_t*)pValue);
ALOGV("Agc2SetParameter() extra saturation margin %f dB", valueFloat);
- effect->session->config.gain_controller2.adaptive_digital.extra_saturation_margin_db =
- valueFloat;
+ if (valueFloat != 2.0) {
+ // extra_staturation_margin_db is no longer configurable in webrtc
+ status = -EINVAL;
+ }
break;
case AGC2_PARAM_PROPERTIES:
ALOGV("Agc2SetParameter() properties gain %f, level %d margin %f",
@@ -455,11 +451,9 @@
pProperties->extraSaturationMargin);
effect->session->config.gain_controller2.fixed_digital.gain_db =
pProperties->fixedDigitalGain;
- effect->session->config.gain_controller2.adaptive_digital.level_estimator =
- (webrtc::AudioProcessing::Config::GainController2::LevelEstimator)
- pProperties->level_estimator;
- effect->session->config.gain_controller2.adaptive_digital.extra_saturation_margin_db =
- pProperties->extraSaturationMargin;
+ if (pProperties->level_estimator != 0 || pProperties->extraSaturationMargin != 2.0) {
+ status = -EINVAL;
+ }
break;
default:
ALOGW("Agc2SetParameter() unknown param %08x value %08x", param, *(uint32_t*)pValue);
@@ -879,8 +873,8 @@
error:
if (session->createdMsk == 0) {
- delete session->apm;
- session->apm = NULL;
+ // Scoped_refptr will handle reference counting here
+ session->apm = nullptr;
}
return status;
}
@@ -889,8 +883,8 @@
ALOGW_IF(Effect_Release(fx) != 0, " Effect_Release() failed for proc ID %d", fx->procId);
session->createdMsk &= ~(1 << fx->procId);
if (session->createdMsk == 0) {
- delete session->apm;
- session->apm = NULL;
+ // Scoped_refptr will handle reference counting here
+ session->apm = nullptr;
session->id = 0;
}
diff --git a/media/libeffects/preprocessing/tests/EffectPreprocessingTest.cpp b/media/libeffects/preprocessing/tests/EffectPreprocessingTest.cpp
index 07006a1..8fdb864 100644
--- a/media/libeffects/preprocessing/tests/EffectPreprocessingTest.cpp
+++ b/media/libeffects/preprocessing/tests/EffectPreprocessingTest.cpp
@@ -143,10 +143,6 @@
const AGC2Params* agc2Params = ¶ms->agc2Params;
ASSERT_NO_FATAL_FAILURE(
effect.setParam(AGC2_PARAM_FIXED_DIGITAL_GAIN, agc2Params->fixedDigitalGain));
- ASSERT_NO_FATAL_FAILURE(effect.setParam(AGC2_PARAM_ADAPT_DIGI_LEVEL_ESTIMATOR,
- agc2Params->adaptDigiLevelEstimator));
- ASSERT_NO_FATAL_FAILURE(effect.setParam(AGC2_PARAM_ADAPT_DIGI_EXTRA_SATURATION_MARGIN,
- agc2Params->extraSaturationMargin));
} else if (isAECEffect(uuid)) {
const AECParams* aecParams = ¶ms->aecParams;
ASSERT_NO_FATAL_FAILURE(effect.setParam(AEC_PARAM_ECHO_DELAY, aecParams->echoDelay));
diff --git a/media/libeffects/preprocessing/tests/PreProcessingTest.cpp b/media/libeffects/preprocessing/tests/PreProcessingTest.cpp
index 3bd93f8..03400d7 100644
--- a/media/libeffects/preprocessing/tests/PreProcessingTest.cpp
+++ b/media/libeffects/preprocessing/tests/PreProcessingTest.cpp
@@ -400,20 +400,6 @@
ALOGE("Invalid AGC2 Fixed Digital Gain. Error %d\n", status);
return EXIT_FAILURE;
}
- if (int status = preProcSetConfigParam(AGC2_PARAM_ADAPT_DIGI_LEVEL_ESTIMATOR,
- (uint32_t)preProcCfgParams.agc2Level,
- effectHandle[PREPROC_AGC2]);
- status != 0) {
- ALOGE("Invalid AGC2 Level Estimator. Error %d\n", status);
- return EXIT_FAILURE;
- }
- if (int status = preProcSetConfigParam(AGC2_PARAM_ADAPT_DIGI_EXTRA_SATURATION_MARGIN,
- (float)preProcCfgParams.agc2SaturationMargin,
- effectHandle[PREPROC_AGC2]);
- status != 0) {
- ALOGE("Invalid AGC2 Saturation Margin. Error %d\n", status);
- return EXIT_FAILURE;
- }
}
if (effectEn[PREPROC_NS]) {
if (int status = preProcSetConfigParam(NS_PARAM_LEVEL, (uint32_t)preProcCfgParams.nsLevel,
diff --git a/media/libeffects/visualizer/Android.bp b/media/libeffects/visualizer/Android.bp
index 8dd6789..fb9d7b7 100644
--- a/media/libeffects/visualizer/Android.bp
+++ b/media/libeffects/visualizer/Android.bp
@@ -18,34 +18,59 @@
],
}
-cc_library_shared {
- name: "libvisualizer",
-
+cc_defaults {
+ name: "visualizer_defaults",
vendor: true,
-
- srcs: [
- "EffectVisualizer.cpp",
- ],
-
cflags: [
- "-O2",
- "-fvisibility=hidden",
-
- "-DBUILD_FLOAT",
+ "-DBUILD_FLOAT", // TODO: remove BUILD_FLOAT and SUPPORT_MC in lvm libs
"-DSUPPORT_MC",
-
"-Wall",
"-Werror",
],
-
shared_libs: [
"liblog",
],
-
- relative_install_path: "soundfx",
-
header_libs: [
"libaudioeffects",
"libaudioutils_headers",
],
}
+
+cc_library_shared {
+ name: "libvisualizer",
+ defaults: [
+ "visualizer_defaults",
+ ],
+ srcs: [
+ "EffectVisualizer.cpp",
+ ],
+ relative_install_path: "soundfx",
+ cflags: [
+ "-O2",
+ "-fvisibility=hidden",
+ ],
+}
+
+cc_library_shared {
+ name: "libvisualizeraidl",
+ srcs: [
+ "aidl/Visualizer.cpp",
+ "aidl/VisualizerContext.cpp",
+ ":effectCommonFile",
+ ],
+ defaults: [
+ "aidlaudioeffectservice_defaults",
+ "latest_android_hardware_audio_effect_ndk_shared",
+ "latest_android_media_audio_common_types_ndk_shared",
+ "visualizer_defaults",
+ ],
+ cflags: [
+ "-Wthread-safety",
+ ],
+ shared_libs: [
+ "libcutils",
+ ],
+ visibility: [
+ "//hardware/interfaces/audio/aidl/default",
+ ],
+}
diff --git a/media/libeffects/visualizer/aidl/Visualizer.cpp b/media/libeffects/visualizer/aidl/Visualizer.cpp
new file mode 100644
index 0000000..28a7287
--- /dev/null
+++ b/media/libeffects/visualizer/aidl/Visualizer.cpp
@@ -0,0 +1,258 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "AHAL_VisualizerLibEffects"
+
+#include <android-base/logging.h>
+#include "Visualizer.h"
+
+using aidl::android::hardware::audio::effect::Descriptor;
+using aidl::android::hardware::audio::effect::IEffect;
+using aidl::android::hardware::audio::effect::VisualizerImpl;
+using aidl::android::hardware::audio::effect::kVisualizerImplUUID;
+using aidl::android::hardware::audio::effect::State;
+using aidl::android::media::audio::common::AudioUuid;
+
+extern "C" binder_exception_t createEffect(const AudioUuid* in_impl_uuid,
+ std::shared_ptr<IEffect>* instanceSpp) {
+ if (!in_impl_uuid || *in_impl_uuid != kVisualizerImplUUID) {
+ LOG(ERROR) << __func__ << "uuid not supported";
+ return EX_ILLEGAL_ARGUMENT;
+ }
+ if (instanceSpp) {
+ *instanceSpp = ndk::SharedRefBase::make<VisualizerImpl>();
+ LOG(DEBUG) << __func__ << " instance " << instanceSpp->get() << " created";
+ return EX_NONE;
+ } else {
+ LOG(ERROR) << __func__ << " invalid input parameter!";
+ return EX_ILLEGAL_ARGUMENT;
+ }
+}
+
+extern "C" binder_exception_t queryEffect(const AudioUuid* in_impl_uuid, Descriptor* _aidl_return) {
+ if (!in_impl_uuid || *in_impl_uuid != kVisualizerImplUUID) {
+ LOG(ERROR) << __func__ << "uuid not supported";
+ return EX_ILLEGAL_ARGUMENT;
+ }
+ *_aidl_return = VisualizerImpl::kDescriptor;
+ return EX_NONE;
+}
+
+namespace aidl::android::hardware::audio::effect {
+
+const std::string VisualizerImpl::kEffectName = "Visualizer";
+const Visualizer::Capability VisualizerImpl::kCapability = {
+ .maxLatencyMs = VisualizerContext::kMaxLatencyMs,
+ .captureSampleRange = {.min = 0, .max = VisualizerContext::kMaxCaptureBufSize}};
+const Descriptor VisualizerImpl::kDescriptor = {
+ .common = {.id = {.type = kVisualizerTypeUUID,
+ .uuid = kVisualizerImplUUID,
+ .proxy = std::nullopt},
+ .flags = {.type = Flags::Type::INSERT,
+ .insert = Flags::Insert::LAST,
+ .volume = Flags::Volume::CTRL},
+ .name = VisualizerImpl::kEffectName,
+ .implementor = "The Android Open Source Project"},
+ .capability = Capability::make<Capability::visualizer>(VisualizerImpl::kCapability)};
+
+ndk::ScopedAStatus VisualizerImpl::getDescriptor(Descriptor* _aidl_return) {
+ RETURN_IF(!_aidl_return, EX_ILLEGAL_ARGUMENT, "Parameter:nullptr");
+ LOG(DEBUG) << __func__ << kDescriptor.toString();
+ *_aidl_return = kDescriptor;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus VisualizerImpl::commandImpl(CommandId command) {
+ RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+ switch (command) {
+ case CommandId::START:
+ mContext->enable();
+ break;
+ case CommandId::STOP:
+ mContext->disable();
+ break;
+ case CommandId::RESET:
+ mContext->disable();
+ mContext->resetBuffer();
+ break;
+ default:
+ LOG(ERROR) << __func__ << " commandId " << toString(command) << " not supported";
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "commandIdNotSupported");
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus VisualizerImpl::setOnlyParameter(
+ const Visualizer::SetOnlyParameters& param) {
+ auto tag = param.getTag();
+ switch (tag) {
+ case Visualizer::SetOnlyParameters::latencyMs: {
+ RETURN_IF(mContext->setDownstreamLatency(
+ param.get<Visualizer::SetOnlyParameters::latencyMs>()) !=
+ RetCode::SUCCESS,
+ EX_ILLEGAL_ARGUMENT, "setLatencyFailed");
+ break;
+ }
+ default: {
+ LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag);
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+ EX_ILLEGAL_ARGUMENT, "setOnlyParameterTagNotSupported");
+ }
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus VisualizerImpl::setParameterSpecific(const Parameter::Specific& specific) {
+ RETURN_IF(Parameter::Specific::visualizer != specific.getTag(), EX_ILLEGAL_ARGUMENT,
+ "EffectNotSupported");
+ RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+
+ auto& param = specific.get<Parameter::Specific::visualizer>();
+ const auto tag = param.getTag();
+ switch (tag) {
+ case Visualizer::captureSamples: {
+ RETURN_IF(mContext->setCaptureSamples(param.get<Visualizer::captureSamples>()) !=
+ RetCode::SUCCESS,
+ EX_ILLEGAL_ARGUMENT, "setCaptureSizeFailed");
+ return ndk::ScopedAStatus::ok();
+ }
+ case Visualizer::scalingMode: {
+ RETURN_IF(mContext->setScalingMode(param.get<Visualizer::scalingMode>()) !=
+ RetCode::SUCCESS,
+ EX_ILLEGAL_ARGUMENT, "setScalingModeFailed");
+ return ndk::ScopedAStatus::ok();
+ }
+ case Visualizer::measurementMode: {
+ RETURN_IF(mContext->setMeasurementMode(param.get<Visualizer::measurementMode>()) !=
+ RetCode::SUCCESS,
+ EX_ILLEGAL_ARGUMENT, "setMeasurementModeFailed");
+ return ndk::ScopedAStatus::ok();
+ }
+ case Visualizer::setOnlyParameters: {
+ return setOnlyParameter(param.get<Visualizer::setOnlyParameters>());
+ }
+ default: {
+ LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag);
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+ EX_ILLEGAL_ARGUMENT, "VisualizerTagNotSupported");
+ }
+ }
+}
+
+ndk::ScopedAStatus VisualizerImpl::getOnlyParameter(const Visualizer::GetOnlyParameters::Tag tag,
+ Parameter::Specific* specific) {
+ Visualizer visualizer;
+ Visualizer::GetOnlyParameters param;
+ switch (tag) {
+ case Visualizer::GetOnlyParameters::measurement: {
+ param.set<Visualizer::GetOnlyParameters::measurement>(mContext->getMeasure());
+ break;
+ }
+ case Visualizer::GetOnlyParameters::captureSampleBuffer: {
+ param.set<Visualizer::GetOnlyParameters::captureSampleBuffer>(mContext->capture());
+ break;
+ }
+ default: {
+ LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag);
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+ EX_ILLEGAL_ARGUMENT, "setOnlyParameterTagNotSupported");
+ }
+ }
+ visualizer.set<Visualizer::getOnlyParameters>(param);
+ specific->set<Parameter::Specific::visualizer>(visualizer);
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus VisualizerImpl::getParameterSpecific(const Parameter::Id& id,
+ Parameter::Specific* specific) {
+ RETURN_IF(!specific, EX_NULL_POINTER, "nullPtr");
+ auto tag = id.getTag();
+ RETURN_IF(Parameter::Id::visualizerTag != tag, EX_ILLEGAL_ARGUMENT, "wrongIdTag");
+ auto specificId = id.get<Parameter::Id::visualizerTag>();
+ auto specificTag = specificId.getTag();
+ switch (specificTag) {
+ case Visualizer::Id::commonTag: {
+ return getParameterVisualizer(specificId.get<Visualizer::Id::commonTag>(), specific);
+ }
+ case Visualizer::Id::getOnlyParamTag: {
+ return getOnlyParameter(specificId.get<Visualizer::Id::getOnlyParamTag>(), specific);
+ }
+ default: {
+ LOG(ERROR) << __func__ << " unsupported tag: " << toString(specificTag);
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "VisualizerTagNotSupported");
+ }
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus VisualizerImpl::getParameterVisualizer(const Visualizer::Tag& tag,
+ Parameter::Specific* specific) {
+ RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+
+ Visualizer param;
+ switch (tag) {
+ case Visualizer::captureSamples: {
+ param.set<Visualizer::captureSamples>(mContext->getCaptureSamples());
+ break;
+ }
+ case Visualizer::scalingMode: {
+ param.set<Visualizer::scalingMode>(mContext->getScalingMode());
+ break;
+ }
+ case Visualizer::measurementMode: {
+ param.set<Visualizer::measurementMode>(mContext->getMeasurementMode());
+ break;
+ }
+ default: {
+ LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag);
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+ EX_ILLEGAL_ARGUMENT, "VisualizerTagNotSupported");
+ }
+ }
+
+ specific->set<Parameter::Specific::visualizer>(param);
+ return ndk::ScopedAStatus::ok();
+}
+
+std::shared_ptr<EffectContext> VisualizerImpl::createContext(const Parameter::Common& common) {
+ if (mContext) {
+ LOG(DEBUG) << __func__ << " context already exist";
+ return mContext;
+ }
+
+ mContext = std::make_shared<VisualizerContext>(1 /* statusFmqDepth */, common);
+ mContext->initParams(common);
+ return mContext;
+}
+
+RetCode VisualizerImpl::releaseContext() {
+ if (mContext) {
+ mContext->disable();
+ mContext->resetBuffer();
+ }
+ return RetCode::SUCCESS;
+}
+
+// Processing method running in EffectWorker thread.
+IEffect::Status VisualizerImpl::effectProcessImpl(float* in, float* out, int samples) {
+ IEffect::Status status = {EX_NULL_POINTER, 0, 0};
+ RETURN_VALUE_IF(!mContext, status, "nullContext");
+ return mContext->process(in, out, samples);
+}
+
+} // namespace aidl::android::hardware::audio::effect
diff --git a/media/libeffects/visualizer/aidl/Visualizer.h b/media/libeffects/visualizer/aidl/Visualizer.h
new file mode 100644
index 0000000..5908d9a
--- /dev/null
+++ b/media/libeffects/visualizer/aidl/Visualizer.h
@@ -0,0 +1,60 @@
+/*
+ * 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/effect/BnEffect.h>
+
+#include "effect-impl/EffectImpl.h"
+#include "effect-impl/EffectUUID.h"
+
+#include "VisualizerContext.h"
+
+namespace aidl::android::hardware::audio::effect {
+
+class VisualizerImpl final : public EffectImpl {
+ public:
+ static const std::string kEffectName;
+ static const Visualizer::Capability kCapability;
+ static const Descriptor kDescriptor;
+ VisualizerImpl() { LOG(DEBUG) << __func__; }
+ ~VisualizerImpl() {
+ cleanUp();
+ LOG(DEBUG) << __func__;
+ }
+
+ ndk::ScopedAStatus commandImpl(CommandId command) override;
+ ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
+ ndk::ScopedAStatus setParameterSpecific(const Parameter::Specific& specific) override;
+ ndk::ScopedAStatus getParameterSpecific(const Parameter::Id& id,
+ Parameter::Specific* specific) override;
+ IEffect::Status effectProcessImpl(float* in, float* out, int process) override;
+ std::shared_ptr<EffectContext> createContext(const Parameter::Common& common) override;
+ RetCode releaseContext() override;
+
+ std::shared_ptr<EffectContext> getContext() override { return mContext; }
+ std::string getEffectName() override { return kEffectName; }
+
+ private:
+ std::shared_ptr<VisualizerContext> mContext;
+ ndk::ScopedAStatus getParameterVisualizer(const Visualizer::Tag& tag,
+ Parameter::Specific* specific);
+ ndk::ScopedAStatus setOnlyParameter(const Visualizer::SetOnlyParameters& param);
+ ndk::ScopedAStatus getOnlyParameter(const Visualizer::GetOnlyParameters::Tag tag,
+ Parameter::Specific* specific);
+};
+
+} // namespace aidl::android::hardware::audio::effect
diff --git a/media/libeffects/visualizer/aidl/VisualizerContext.cpp b/media/libeffects/visualizer/aidl/VisualizerContext.cpp
new file mode 100644
index 0000000..1965e0e
--- /dev/null
+++ b/media/libeffects/visualizer/aidl/VisualizerContext.cpp
@@ -0,0 +1,331 @@
+/*
+ * 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.
+ */
+
+#include "VisualizerContext.h"
+
+#include <algorithm>
+#include <android/binder_status.h>
+#include <audio_utils/primitives.h>
+#include <math.h>
+#include <system/audio.h>
+#include <time.h>
+#include <Utils.h>
+
+#ifndef BUILD_FLOAT
+ #error AIDL Visualizer only support float 32bits, make sure add cflags -DBUILD_FLOAT,
+#endif
+
+using android::hardware::audio::common::getChannelCount;
+
+namespace aidl::android::hardware::audio::effect {
+
+VisualizerContext::VisualizerContext(int statusDepth, const Parameter::Common& common)
+ : EffectContext(statusDepth, common) {
+}
+
+VisualizerContext::~VisualizerContext() {
+ std::lock_guard lg(mMutex);
+ LOG(DEBUG) << __func__;
+ mState = State::UNINITIALIZED;
+}
+
+RetCode VisualizerContext::initParams(const Parameter::Common& common) {
+ std::lock_guard lg(mMutex);
+ LOG(DEBUG) << __func__;
+ if (common.input != common.output) {
+ LOG(ERROR) << __func__ << " mismatch input: " << common.input.toString()
+ << " and output: " << common.output.toString();
+ return RetCode::ERROR_ILLEGAL_PARAMETER;
+ }
+
+ mState = State::INITIALIZED;
+ auto channelCount = getChannelCount(common.input.base.channelMask);
+#ifdef SUPPORT_MC
+ if (channelCount < 1 || channelCount > FCC_LIMIT) return RetCode::ERROR_ILLEGAL_PARAMETER;
+#else
+ if (channelCount != FCC_2) return RetCode::ERROR_ILLEGAL_PARAMETER;
+#endif
+ mChannelCount = channelCount;
+ mCommon = common;
+ return RetCode::SUCCESS;
+}
+
+RetCode VisualizerContext::enable() {
+ std::lock_guard lg(mMutex);
+ if (mState != State::INITIALIZED) {
+ return RetCode::ERROR_EFFECT_LIB_ERROR;
+ }
+ mState = State::ACTIVE;
+ return RetCode::SUCCESS;
+}
+
+RetCode VisualizerContext::disable() {
+ std::lock_guard lg(mMutex);
+ if (mState != State::ACTIVE) {
+ return RetCode::ERROR_EFFECT_LIB_ERROR;
+ }
+ mState = State::INITIALIZED;
+ return RetCode::SUCCESS;
+}
+
+void VisualizerContext::reset() {
+ std::lock_guard lg(mMutex);
+ std::fill_n(mCaptureBuf.begin(), kMaxCaptureBufSize, 0x80);
+}
+
+RetCode VisualizerContext::setCaptureSamples(int samples) {
+ std::lock_guard lg(mMutex);
+ if (samples < 0 || (unsigned)samples > kMaxCaptureBufSize) {
+ LOG(ERROR) << __func__ << " captureSamples " << samples << " exceed valid range: 0 - "
+ << kMaxCaptureBufSize;
+ return RetCode::ERROR_ILLEGAL_PARAMETER;
+ }
+ mCaptureSamples = samples;
+ return RetCode::SUCCESS;
+}
+int VisualizerContext::getCaptureSamples() {
+ std::lock_guard lg(mMutex);
+ return mCaptureSamples;
+}
+
+RetCode VisualizerContext::setMeasurementMode(Visualizer::MeasurementMode mode) {
+ std::lock_guard lg(mMutex);
+ mMeasurementMode = mode;
+ return RetCode::SUCCESS;
+}
+Visualizer::MeasurementMode VisualizerContext::getMeasurementMode() {
+ std::lock_guard lg(mMutex);
+ return mMeasurementMode;
+}
+
+RetCode VisualizerContext::setScalingMode(Visualizer::ScalingMode mode) {
+ std::lock_guard lg(mMutex);
+ mScalingMode = mode;
+ return RetCode::SUCCESS;
+}
+Visualizer::ScalingMode VisualizerContext::getScalingMode() {
+ std::lock_guard lg(mMutex);
+ return mScalingMode;
+}
+
+RetCode VisualizerContext::setDownstreamLatency(int latency) {
+ if (latency < 0 || (unsigned)latency > kMaxLatencyMs) {
+ LOG(ERROR) << __func__ << " latency " << latency << " exceed valid range: 0 - "
+ << kMaxLatencyMs;
+ return RetCode::ERROR_ILLEGAL_PARAMETER;
+ }
+ std::lock_guard lg(mMutex);
+ mDownstreamLatency = latency;
+ return RetCode::SUCCESS;
+}
+
+uint32_t VisualizerContext::getDeltaTimeMsFromUpdatedTime_l() {
+ uint32_t deltaMs = 0;
+ if (mBufferUpdateTime.tv_sec != 0) {
+ struct timespec ts;
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
+ time_t secs = ts.tv_sec - mBufferUpdateTime.tv_sec;
+ long nsec = ts.tv_nsec - mBufferUpdateTime.tv_nsec;
+ if (nsec < 0) {
+ --secs;
+ nsec += 1000000000;
+ }
+ deltaMs = secs * 1000 + nsec / 1000000;
+ }
+ }
+ return deltaMs;
+}
+
+Visualizer::GetOnlyParameters::Measurement VisualizerContext::getMeasure() {
+ uint16_t peakU16 = 0;
+ float sumRmsSquared = 0.0f;
+ uint8_t nbValidMeasurements = 0;
+
+ {
+ std::lock_guard lg(mMutex);
+ // reset measurements if last measurement was too long ago (which implies stored
+ // measurements aren't relevant anymore and shouldn't bias the new one)
+ const uint32_t delayMs = getDeltaTimeMsFromUpdatedTime_l();
+ if (delayMs > kDiscardMeasurementsTimeMs) {
+ LOG(INFO) << __func__ << " Discarding " << delayMs << " ms old measurements";
+ for (uint32_t i = 0; i < mMeasurementWindowSizeInBuffers; i++) {
+ mPastMeasurements[i].mIsValid = false;
+ mPastMeasurements[i].mPeakU16 = 0;
+ mPastMeasurements[i].mRmsSquared = 0;
+ }
+ mMeasurementBufferIdx = 0;
+ } else {
+ // only use actual measurements, otherwise the first RMS measure happening before
+ // MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS have been played will always be artificially
+ // low
+ for (uint32_t i = 0; i < mMeasurementWindowSizeInBuffers; i++) {
+ if (mPastMeasurements[i].mIsValid) {
+ if (mPastMeasurements[i].mPeakU16 > peakU16) {
+ peakU16 = mPastMeasurements[i].mPeakU16;
+ }
+ sumRmsSquared += mPastMeasurements[i].mRmsSquared;
+ nbValidMeasurements++;
+ }
+ }
+ }
+ }
+
+ float rms = nbValidMeasurements == 0 ? 0.0f : sqrtf(sumRmsSquared / nbValidMeasurements);
+ Visualizer::GetOnlyParameters::Measurement measure;
+ // convert from I16 sample values to mB and write results
+ measure.rms = (rms < 0.000016f) ? -9600 : (int32_t)(2000 * log10(rms / 32767.0f));
+ measure.peak = (peakU16 == 0) ? -9600 : (int32_t)(2000 * log10(peakU16 / 32767.0f));
+ LOG(INFO) << __func__ << " peak " << peakU16 << " (" << measure.peak << "mB), rms " << rms
+ << " (" << measure.rms << "mB)";
+ return measure;
+}
+
+std::vector<uint8_t> VisualizerContext::capture() {
+ std::vector<uint8_t> result;
+ std::lock_guard lg(mMutex);
+ RETURN_VALUE_IF(mState != State::ACTIVE, result, "illegalState");
+ const uint32_t deltaMs = getDeltaTimeMsFromUpdatedTime_l();
+
+ // if audio framework has stopped playing audio although the effect is still active we must
+ // clear the capture buffer to return silence
+ if ((mLastCaptureIdx == mCaptureIdx) && (mBufferUpdateTime.tv_sec != 0) &&
+ (deltaMs > kMaxStallTimeMs)) {
+ LOG(INFO) << __func__ << " capture going to idle";
+ mBufferUpdateTime.tv_sec = 0;
+ return result;
+ }
+ int32_t latencyMs = mDownstreamLatency;
+ latencyMs -= deltaMs;
+ if (latencyMs < 0) {
+ latencyMs = 0;
+ }
+ uint32_t deltaSamples = mCaptureSamples + mCommon.input.base.sampleRate * latencyMs / 1000;
+
+ // large sample rate, latency, or capture size, could cause overflow.
+ // do not offset more than the size of buffer.
+ if (deltaSamples > kMaxCaptureBufSize) {
+ android_errorWriteLog(0x534e4554, "31781965");
+ deltaSamples = kMaxCaptureBufSize;
+ }
+
+ int32_t capturePoint;
+ //capturePoint = (int32_t)mCaptureIdx - deltaSamples;
+ __builtin_sub_overflow((int32_t) mCaptureIdx, deltaSamples, &capturePoint);
+ // a negative capturePoint means we wrap the buffer.
+ if (capturePoint < 0) {
+ uint32_t size = -capturePoint;
+ if (size > mCaptureSamples) {
+ size = mCaptureSamples;
+ }
+ result.insert(result.end(), &mCaptureBuf[kMaxCaptureBufSize + capturePoint],
+ &mCaptureBuf[kMaxCaptureBufSize + capturePoint + size]);
+ mCaptureSamples -= size;
+ capturePoint = 0;
+ }
+ result.insert(result.end(), &mCaptureBuf[capturePoint],
+ &mCaptureBuf[capturePoint + mCaptureSamples]);
+ mLastCaptureIdx = mCaptureIdx;
+ return result;
+}
+
+IEffect::Status VisualizerContext::process(float* in, float* out, int samples) {
+ IEffect::Status result = {STATUS_NOT_ENOUGH_DATA, 0, 0};
+ RETURN_VALUE_IF(in == nullptr || out == nullptr || samples == 0, result, "dataBufferError");
+
+ std::lock_guard lg(mMutex);
+ result.status = STATUS_INVALID_OPERATION;
+ RETURN_VALUE_IF(mState != State::ACTIVE, result, "stateNotActive");
+ LOG(DEBUG) << __func__ << " in " << in << " out " << out << " sample " << samples;
+ // perform measurements if needed
+ if (mMeasurementMode == Visualizer::MeasurementMode::PEAK_RMS) {
+ // find the peak and RMS squared for the new buffer
+ float rmsSqAcc = 0;
+ float maxSample = 0.f;
+ for (size_t inIdx = 0; inIdx < (unsigned)samples; ++inIdx) {
+ maxSample = fmax(maxSample, fabs(in[inIdx]));
+ rmsSqAcc += in[inIdx] * in[inIdx];
+ }
+ maxSample *= 1 << 15; // scale to int16_t, with exactly 1 << 15 representing positive num.
+ rmsSqAcc *= 1 << 30; // scale to int16_t * 2
+ mPastMeasurements[mMeasurementBufferIdx] = {
+ .mPeakU16 = (uint16_t)maxSample,
+ .mRmsSquared = rmsSqAcc / samples,
+ .mIsValid = true };
+ if (++mMeasurementBufferIdx >= mMeasurementWindowSizeInBuffers) {
+ mMeasurementBufferIdx = 0;
+ }
+ }
+
+ float fscale; // multiplicative scale
+ if (mScalingMode == Visualizer::ScalingMode::NORMALIZED) {
+ // derive capture scaling factor from peak value in current buffer
+ // this gives more interesting captures for display.
+ float maxSample = 0.f;
+ for (size_t inIdx = 0; inIdx < (unsigned)samples; ) {
+ // we reconstruct the actual summed value to ensure proper normalization
+ // for multichannel outputs (channels > 2 may often be 0).
+ float smp = 0.f;
+ for (int i = 0; i < mChannelCount; ++i) {
+ smp += in[inIdx++];
+ }
+ maxSample = fmax(maxSample, fabs(smp));
+ }
+ if (maxSample > 0.f) {
+ fscale = 0.99f / maxSample;
+ int exp; // unused
+ const float significand = frexp(fscale, &exp);
+ if (significand == 0.5f) {
+ fscale *= 255.f / 256.f; // avoid returning unaltered PCM signal
+ }
+ } else {
+ // scale doesn't matter, the values are all 0.
+ fscale = 1.f;
+ }
+ } else {
+ assert(mScalingMode == Visualizer::ScalingMode::AS_PLAYED);
+ // Note: if channels are uncorrelated, 1/sqrt(N) could be used at the risk of clipping.
+ fscale = 1.f / mChannelCount; // account for summing all the channels together.
+ }
+
+ uint32_t captIdx;
+ uint32_t inIdx;
+ for (inIdx = 0, captIdx = mCaptureIdx; inIdx < (unsigned)samples; captIdx++) {
+ // wrap
+ if (captIdx >= kMaxCaptureBufSize) {
+ captIdx = 0;
+ }
+
+ float smp = 0.f;
+ for (uint32_t i = 0; i < mChannelCount; ++i) {
+ smp += in[inIdx++];
+ }
+ mCaptureBuf[captIdx] = clamp8_from_float(smp * fscale);
+ }
+
+ // the following two should really be atomic, though it probably doesn't
+ // matter much for visualization purposes
+ mCaptureIdx = captIdx;
+ // update last buffer update time stamp
+ if (clock_gettime(CLOCK_MONOTONIC, &mBufferUpdateTime) < 0) {
+ mBufferUpdateTime.tv_sec = 0;
+ }
+
+ // TODO: handle access_mode
+ memcpy(out, in, samples * sizeof(float));
+ return {STATUS_OK, samples, samples};
+}
+
+} // namespace aidl::android::hardware::audio::effect
diff --git a/media/libeffects/visualizer/aidl/VisualizerContext.h b/media/libeffects/visualizer/aidl/VisualizerContext.h
new file mode 100644
index 0000000..bfda0b9
--- /dev/null
+++ b/media/libeffects/visualizer/aidl/VisualizerContext.h
@@ -0,0 +1,101 @@
+/*
+ * 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-base/thread_annotations.h>
+#include <audio_effects/effect_dynamicsprocessing.h>
+
+#include "effect-impl/EffectContext.h"
+
+namespace aidl::android::hardware::audio::effect {
+
+class VisualizerContext final : public EffectContext {
+ public:
+ static const uint32_t kMaxCaptureBufSize = 65536;
+ static const uint32_t kMaxLatencyMs = 3000; // 3 seconds of latency for audio pipeline
+
+ VisualizerContext(int statusDepth, const Parameter::Common& common);
+ ~VisualizerContext();
+
+ RetCode initParams(const Parameter::Common& common);
+
+ RetCode enable();
+ RetCode disable();
+ // keep all parameters and reset buffer.
+ void reset();
+
+ RetCode setCaptureSamples(int captureSize);
+ int getCaptureSamples();
+ RetCode setMeasurementMode(Visualizer::MeasurementMode mode);
+ Visualizer::MeasurementMode getMeasurementMode();
+ RetCode setScalingMode(Visualizer::ScalingMode mode);
+ Visualizer::ScalingMode getScalingMode();
+ RetCode setDownstreamLatency(int latency);
+
+ IEffect::Status process(float* in, float* out, int samples);
+ // Gets the current measurements, measured by process() and consumed by getParameter()
+ Visualizer::GetOnlyParameters::Measurement getMeasure();
+ // Gets the latest PCM capture, data captured by process() and consumed by getParameter()
+ std::vector<uint8_t> capture();
+
+ struct BufferStats {
+ bool mIsValid;
+ uint16_t mPeakU16; // the positive peak of the absolute value of the samples in a buffer
+ float mRmsSquared; // the average square of the samples in a buffer
+ };
+
+ enum State {
+ UNINITIALIZED,
+ INITIALIZED,
+ ACTIVE,
+ };
+
+ private:
+ // maximum time since last capture buffer update before resetting capture buffer. This means
+ // that the framework has stopped playing audio and we must start returning silence
+ static const uint32_t kMaxStallTimeMs = 1000;
+ // discard measurements older than this number of ms
+ static const uint32_t kDiscardMeasurementsTimeMs = 2000;
+ // maximum number of buffers for which we keep track of the measurements
+ // note: buffer index is stored in uint8_t
+ static const uint32_t kMeasurementWindowMaxSizeInBuffers = 25;
+
+ // serialize process() and parameter setting
+ std::mutex mMutex;
+ Parameter::Common mCommon GUARDED_BY(mMutex);
+ State mState GUARDED_BY(mMutex) = State::UNINITIALIZED;
+ uint32_t mCaptureIdx GUARDED_BY(mMutex) = 0;
+ uint32_t mLastCaptureIdx GUARDED_BY(mMutex) = 0;
+ Visualizer::ScalingMode mScalingMode GUARDED_BY(mMutex) = Visualizer::ScalingMode::NORMALIZED;
+ struct timespec mBufferUpdateTime GUARDED_BY(mMutex);
+ // capture buf with 8 bits PCM
+ std::array<uint8_t, kMaxCaptureBufSize> mCaptureBuf GUARDED_BY(mMutex);
+ uint32_t mDownstreamLatency GUARDED_BY(mMutex) = 0;
+ uint32_t mCaptureSamples GUARDED_BY(mMutex) = kMaxCaptureBufSize;
+
+ // to avoid recomputing it every time a buffer is processed
+ uint8_t mChannelCount GUARDED_BY(mMutex) = 0;
+ Visualizer::MeasurementMode mMeasurementMode GUARDED_BY(mMutex) =
+ Visualizer::MeasurementMode::NONE;
+ uint8_t mMeasurementWindowSizeInBuffers = kMeasurementWindowMaxSizeInBuffers;
+ uint8_t mMeasurementBufferIdx GUARDED_BY(mMutex) = 0;
+ std::array<BufferStats, kMeasurementWindowMaxSizeInBuffers> mPastMeasurements;
+ void init_params();
+
+ uint32_t getDeltaTimeMsFromUpdatedTime_l() REQUIRES(mMutex);
+};
+} // namespace aidl::android::hardware::audio::effect
diff --git a/media/libstagefright/OggWriter.cpp b/media/libstagefright/OggWriter.cpp
index d2a65d0..cff37a3 100644
--- a/media/libstagefright/OggWriter.cpp
+++ b/media/libstagefright/OggWriter.cpp
@@ -96,6 +96,7 @@
return ERROR_UNSUPPORTED;
}
+ // NOLINTNEXTLINE(clang-analyzer-unix.MallocSizeof)
mOs = (OggStreamState*) malloc(sizeof(ogg_stream_state));
if (ogg_stream_init((ogg_stream_state*)mOs, rand()) == -1) {
ALOGE("ogg stream init failed");
diff --git a/media/utils/fuzzers/ServiceUtilitiesFuzz.cpp b/media/utils/fuzzers/ServiceUtilitiesFuzz.cpp
index 51e8d7a..15f043a 100644
--- a/media/utils/fuzzers/ServiceUtilitiesFuzz.cpp
+++ b/media/utils/fuzzers/ServiceUtilitiesFuzz.cpp
@@ -25,6 +25,7 @@
static constexpr int kMaxOperations = 50;
static constexpr int kMaxStringLen = 256;
+static constexpr int kMaxSpaces = 1000;
using android::content::AttributionSourceState;
@@ -35,7 +36,9 @@
pm.allowPlaybackCapture(uid);
},
[](FuzzedDataProvider* data_provider, android::MediaPackageManager pm) -> void {
- int spaces = data_provider->ConsumeIntegral<int>();
+ /* The large value of spaces was taking time in file write operation.
+ * Limited spaces values in range to avoid timeout.*/
+ int spaces = data_provider->ConsumeIntegralInRange<int>(0, kMaxSpaces);
// Dump everything into /dev/null
int fd = open("/dev/null", O_WRONLY);
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index d930fb5..f8d7c70 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -2863,14 +2863,15 @@
status_t status = INVALID_OPERATION;
for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
- std::vector<media::MicrophoneInfo> mics;
+ std::vector<audio_microphone_characteristic_t> mics;
AudioHwDevice *dev = mAudioHwDevs.valueAt(i);
mHardwareStatus = AUDIO_HW_GET_MICROPHONES;
status_t devStatus = dev->hwDevice()->getMicrophones(&mics);
mHardwareStatus = AUDIO_HW_IDLE;
if (devStatus == NO_ERROR) {
- microphones->insert(microphones->begin(), mics.begin(), mics.end());
// report success if at least one HW module supports the function.
+ std::transform(mics.begin(), mics.end(), std::back_inserter(*microphones),
+ [](auto& mic) { return media::MicrophoneInfo(mic); });
status = NO_ERROR;
}
}
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 173f0c9..100d8c7 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -140,6 +140,7 @@
class AudioFlinger : public AudioFlingerServerAdapter::Delegate
{
+ friend class sp<AudioFlinger>;
public:
static void instantiate() ANDROID_API;
diff --git a/services/audioflinger/MelReporter.cpp b/services/audioflinger/MelReporter.cpp
index b185959..5ac7cde 100644
--- a/services/audioflinger/MelReporter.cpp
+++ b/services/audioflinger/MelReporter.cpp
@@ -25,7 +25,7 @@
#include <android/binder_manager.h>
#include <utils/Log.h>
-using aidl::android::hardware::audio::core::ISoundDose;
+using aidl::android::hardware::audio::core::sounddose::ISoundDose;
using aidl::android::hardware::audio::sounddose::ISoundDoseFactory;
namespace android {
@@ -60,8 +60,6 @@
return false;
}
- std::lock_guard _l(mLock);
- mUseHalSoundDoseInterface = true;
stopInternalMelComputation();
return true;
}
@@ -77,12 +75,6 @@
}
mSoundDoseManager->setHalSoundDoseInterface(nullptr);
-
- for (const auto& activePatches : mActiveMelPatches) {
- for (const auto& deviceId : activePatches.second.deviceHandles) {
- startMelComputationForNewPatch(activePatches.second.streamHandle, deviceId);
- }
- }
}
void AudioFlinger::MelReporter::onFirstRef() {
@@ -205,16 +197,9 @@
void AudioFlinger::MelReporter::stopInternalMelComputation() {
ALOGV("%s", __func__);
- std::unordered_map<audio_patch_handle_t, ActiveMelPatch> activePatchesCopy;
- {
- std::lock_guard _l(mLock);
- activePatchesCopy = mActiveMelPatches;
- mActiveMelPatches.clear();
- }
-
- for (const auto& activePatch : activePatchesCopy) {
- stopInternalMelComputationForStream(activePatch.second.streamHandle);
- }
+ std::lock_guard _l(mLock);
+ mActiveMelPatches.clear();
+ mUseHalSoundDoseInterface = true;
}
void AudioFlinger::MelReporter::stopInternalMelComputationForStream(audio_io_handle_t streamId) {
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 3dcf932..f850ede 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -2089,8 +2089,7 @@
mHwSupportsPause(false), mHwPaused(false), mFlushPending(false),
mLeftVolFloat(-1.0), mRightVolFloat(-1.0),
mDownStreamPatch{},
- mIsTimestampAdvancing(kMinimumTimeBetweenTimestampChecksNs),
- mBluetoothLatencyModesEnabled(true)
+ mIsTimestampAdvancing(kMinimumTimeBetweenTimestampChecksNs)
{
snprintf(mThreadName, kThreadNameLength, "AudioOut_%X", id);
mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mThreadName);
@@ -2971,7 +2970,14 @@
void AudioFlinger::PlaybackThread::onCodecFormatChanged(
const std::basic_string<uint8_t>& metadataBs)
{
- std::thread([this, metadataBs]() {
+ wp<AudioFlinger::PlaybackThread> weakPointerThis = this;
+ std::thread([this, metadataBs, weakPointerThis]() {
+ sp<AudioFlinger::PlaybackThread> playbackThread = weakPointerThis.promote();
+ if (playbackThread == nullptr) {
+ ALOGW("PlaybackThread was destroyed, skip codec format change event");
+ return;
+ }
+
audio_utils::metadata::Data metadata =
audio_utils::metadata::dataFromByteString(metadataBs);
if (metadata.empty()) {
@@ -4693,6 +4699,8 @@
} else {
status = PlaybackThread::createAudioPatch_l(patch, handle);
}
+
+ updateHalSupportedLatencyModes_l();
return status;
}
@@ -4844,6 +4852,7 @@
: PlaybackThread(audioFlinger, output, id, type, systemReady, mixerConfig),
// mAudioMixer below
// mFastMixer below
+ mBluetoothLatencyModesEnabled(false),
mFastMixerFutex(0),
mMasterMono(false)
// mOutputSink below
@@ -5059,6 +5068,21 @@
delete mAudioMixer;
}
+void AudioFlinger::MixerThread::onFirstRef() {
+ PlaybackThread::onFirstRef();
+
+ Mutex::Autolock _l(mLock);
+ if (mOutput != nullptr && mOutput->stream != nullptr) {
+ status_t status = mOutput->stream->setLatencyModeCallback(this);
+ if (status != INVALID_OPERATION) {
+ updateHalSupportedLatencyModes_l();
+ }
+ // Default to enabled if the HAL supports it. This can be changed by Audioflinger after
+ // the thread construction according to AudioFlinger::mBluetoothLatencyModesEnabled
+ mBluetoothLatencyModesEnabled.store(
+ mOutput->audioHwDev->supportsBluetoothVariableLatency());
+ }
+}
uint32_t AudioFlinger::MixerThread::correctLatency_l(uint32_t latency) const
{
@@ -6268,6 +6292,100 @@
maxPeriod = seconds(mNormalFrameCount) / mSampleRate * 15;
}
+void AudioFlinger::MixerThread::onHalLatencyModesChanged_l() {
+ mAudioFlinger->onSupportedLatencyModesChanged(mId, mSupportedLatencyModes);
+}
+
+void AudioFlinger::MixerThread::setHalLatencyMode_l() {
+ // Only handle latency mode if:
+ // - mBluetoothLatencyModesEnabled is true
+ // - the HAL supports latency modes
+ // - the selected device is Bluetooth LE or A2DP
+ if (!mBluetoothLatencyModesEnabled.load() || mSupportedLatencyModes.empty()) {
+ return;
+ }
+ if (mOutDeviceTypeAddrs.size() != 1
+ || !(audio_is_a2dp_out_device(mOutDeviceTypeAddrs[0].mType)
+ || audio_is_ble_out_device(mOutDeviceTypeAddrs[0].mType))) {
+ return;
+ }
+
+ audio_latency_mode_t latencyMode = AUDIO_LATENCY_MODE_FREE;
+ if (mSupportedLatencyModes.size() == 1) {
+ // If the HAL only support one latency mode currently, confirm the choice
+ latencyMode = mSupportedLatencyModes[0];
+ } else if (mSupportedLatencyModes.size() > 1) {
+ // Request low latency if:
+ // - At least one active track is either:
+ // - a fast track with gaming usage or
+ // - a track with acessibility usage
+ for (const auto& track : mActiveTracks) {
+ if ((track->isFastTrack() && track->attributes().usage == AUDIO_USAGE_GAME)
+ || track->attributes().usage == AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY) {
+ latencyMode = AUDIO_LATENCY_MODE_LOW;
+ break;
+ }
+ }
+ }
+
+ if (latencyMode != mSetLatencyMode) {
+ status_t status = mOutput->stream->setLatencyMode(latencyMode);
+ ALOGD("%s: thread(%d) setLatencyMode(%s) returned %d",
+ __func__, mId, toString(latencyMode).c_str(), status);
+ if (status == NO_ERROR) {
+ mSetLatencyMode = latencyMode;
+ }
+ }
+}
+
+void AudioFlinger::MixerThread::updateHalSupportedLatencyModes_l() {
+
+ if (mOutput == nullptr || mOutput->stream == nullptr) {
+ return;
+ }
+ std::vector<audio_latency_mode_t> latencyModes;
+ const status_t status = mOutput->stream->getRecommendedLatencyModes(&latencyModes);
+ if (status != NO_ERROR) {
+ latencyModes.clear();
+ }
+ if (latencyModes != mSupportedLatencyModes) {
+ ALOGD("%s: thread(%d) status %d supported latency modes: %s",
+ __func__, mId, status, toString(latencyModes).c_str());
+ mSupportedLatencyModes.swap(latencyModes);
+ sendHalLatencyModesChangedEvent_l();
+ }
+}
+
+status_t AudioFlinger::MixerThread::getSupportedLatencyModes(
+ std::vector<audio_latency_mode_t>* modes) {
+ if (modes == nullptr) {
+ return BAD_VALUE;
+ }
+ Mutex::Autolock _l(mLock);
+ *modes = mSupportedLatencyModes;
+ return NO_ERROR;
+}
+
+void AudioFlinger::MixerThread::onRecommendedLatencyModeChanged(
+ std::vector<audio_latency_mode_t> modes) {
+ Mutex::Autolock _l(mLock);
+ if (modes != mSupportedLatencyModes) {
+ ALOGD("%s: thread(%d) supported latency modes: %s",
+ __func__, mId, toString(modes).c_str());
+ mSupportedLatencyModes.swap(modes);
+ sendHalLatencyModesChangedEvent_l();
+ }
+}
+
+status_t AudioFlinger::MixerThread::setBluetoothVariableLatencyEnabled(bool enabled) {
+ if (mOutput == nullptr || mOutput->audioHwDev == nullptr
+ || !mOutput->audioHwDev->supportsBluetoothVariableLatency()) {
+ return INVALID_OPERATION;
+ }
+ mBluetoothLatencyModesEnabled.store(enabled);
+ return NO_ERROR;
+}
+
// ----------------------------------------------------------------------------
AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger,
@@ -7499,13 +7617,7 @@
}
void AudioFlinger::SpatializerThread::onFirstRef() {
- PlaybackThread::onFirstRef();
-
- Mutex::Autolock _l(mLock);
- status_t status = mOutput->stream->setLatencyModeCallback(this);
- if (status != INVALID_OPERATION) {
- updateHalSupportedLatencyModes_l();
- }
+ MixerThread::onFirstRef();
const pid_t tid = getTid();
if (tid == -1) {
@@ -7519,32 +7631,6 @@
}
}
-status_t AudioFlinger::SpatializerThread::createAudioPatch_l(const struct audio_patch *patch,
- audio_patch_handle_t *handle)
-{
- status_t status = MixerThread::createAudioPatch_l(patch, handle);
- updateHalSupportedLatencyModes_l();
- return status;
-}
-
-void AudioFlinger::SpatializerThread::updateHalSupportedLatencyModes_l() {
- std::vector<audio_latency_mode_t> latencyModes;
- const status_t status = mOutput->stream->getRecommendedLatencyModes(&latencyModes);
- if (status != NO_ERROR) {
- latencyModes.clear();
- }
- if (latencyModes != mSupportedLatencyModes) {
- ALOGD("%s: thread(%d) status %d supported latency modes: %s",
- __func__, mId, status, toString(latencyModes).c_str());
- mSupportedLatencyModes.swap(latencyModes);
- sendHalLatencyModesChangedEvent_l();
- }
-}
-
-void AudioFlinger::SpatializerThread::onHalLatencyModesChanged_l() {
- mAudioFlinger->onSupportedLatencyModesChanged(mId, mSupportedLatencyModes);
-}
-
void AudioFlinger::SpatializerThread::setHalLatencyMode_l() {
// if mSupportedLatencyModes is empty, the HAL stream does not support
// latency mode control and we can exit.
@@ -7592,25 +7678,6 @@
return NO_ERROR;
}
-status_t AudioFlinger::SpatializerThread::getSupportedLatencyModes(
- std::vector<audio_latency_mode_t>* modes) {
- if (modes == nullptr) {
- return BAD_VALUE;
- }
- Mutex::Autolock _l(mLock);
- *modes = mSupportedLatencyModes;
- return NO_ERROR;
-}
-
-status_t AudioFlinger::PlaybackThread::setBluetoothVariableLatencyEnabled(bool enabled) {
- if (mOutput == nullptr || mOutput->audioHwDev == nullptr
- || !mOutput->audioHwDev->supportsBluetoothVariableLatency()) {
- return INVALID_OPERATION;
- }
- mBluetoothLatencyModesEnabled.store(enabled);
- return NO_ERROR;
-}
-
void AudioFlinger::SpatializerThread::checkOutputStageEffects()
{
bool hasVirtualizer = false;
@@ -7663,17 +7730,6 @@
}
}
-void AudioFlinger::SpatializerThread::onRecommendedLatencyModeChanged(
- std::vector<audio_latency_mode_t> modes) {
- Mutex::Autolock _l(mLock);
- if (modes != mSupportedLatencyModes) {
- ALOGD("%s: thread(%d) supported latency modes: %s",
- __func__, mId, toString(modes).c_str());
- mSupportedLatencyModes.swap(modes);
- sendHalLatencyModesChangedEvent_l();
- }
-}
-
// ----------------------------------------------------------------------------
// Record
// ----------------------------------------------------------------------------
@@ -7745,10 +7801,11 @@
ALOGV("%p kUseFastCapture = Always, initFastCapture = true", this);
break;
case FastCapture_Static:
- initFastCapture = (mFrameCount * 1000) / mSampleRate < kMinNormalCaptureBufferSizeMs;
- ALOGV("%p kUseFastCapture = Static, (%lld * 1000) / %u vs %u, initFastCapture = %d",
- this, (long long)mFrameCount, mSampleRate, kMinNormalCaptureBufferSizeMs,
- initFastCapture);
+ initFastCapture = !mIsMsdDevice // Disable fast capture for MSD BUS devices.
+ && (mFrameCount * 1000) / mSampleRate < kMinNormalCaptureBufferSizeMs;
+ ALOGV("%p kUseFastCapture = Static, (%lld * 1000) / %u vs %u, initFastCapture = %d "
+ "mIsMsdDevice = %d", this, (long long)mFrameCount, mSampleRate,
+ kMinNormalCaptureBufferSizeMs, initFastCapture, mIsMsdDevice);
break;
// case FastCapture_Dynamic:
}
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 1f0f13a..4ab4557 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -376,8 +376,6 @@
virtual void toAudioPortConfig(struct audio_port_config *config) = 0;
virtual void resizeInputBuffer_l(int32_t maxSharedAudioHistoryMs);
- virtual void onHalLatencyModesChanged_l() {}
-
// see note at declaration of mStandby, mOutDevice and mInDevice
bool standby() const { return mStandby; }
@@ -625,6 +623,8 @@
product_strategy_t getStrategyForStream(audio_stream_type_t stream) const;
+ virtual void onHalLatencyModesChanged_l() {}
+
virtual void dumpInternals_l(int fd __unused, const Vector<String16>& args __unused)
{ }
virtual void dumpTracks_l(int fd __unused, const Vector<String16>& args __unused) { }
@@ -1102,7 +1102,9 @@
return INVALID_OPERATION;
}
- virtual status_t setBluetoothVariableLatencyEnabled(bool enabled);
+ virtual status_t setBluetoothVariableLatencyEnabled(bool enabled __unused) {
+ return INVALID_OPERATION;
+ }
void startMelComputation(const sp<audio_utils::MelProcessor>& processor);
void stopMelComputation();
@@ -1462,12 +1464,10 @@
virtual void flushHw_l() {
mIsTimestampAdvancing.clear();
}
-
- // Bluetooth Variable latency control logic is enabled or disabled for this thread
- std::atomic_bool mBluetoothLatencyModesEnabled;
};
-class MixerThread : public PlaybackThread {
+class MixerThread : public PlaybackThread,
+ public StreamOutHalInterfaceLatencyModeCallback {
public:
MixerThread(const sp<AudioFlinger>& audioFlinger,
AudioStreamOut* output,
@@ -1477,6 +1477,13 @@
audio_config_base_t *mixerConfig = nullptr);
virtual ~MixerThread();
+ // RefBase
+ virtual void onFirstRef();
+
+ // StreamOutHalInterfaceLatencyModeCallback
+ void onRecommendedLatencyModeChanged(
+ std::vector<audio_latency_mode_t> modes) override;
+
// Thread virtuals
virtual bool checkForNewParameter_l(const String8& keyValuePair,
@@ -1513,6 +1520,17 @@
virtual status_t releaseAudioPatch_l(const audio_patch_handle_t handle);
AudioMixer* mAudioMixer; // normal mixer
+
+ // Support low latency mode by default as unless explicitly indicated by the audio HAL
+ // we assume the audio path is compatible with the head tracking latency requirements
+ std::vector<audio_latency_mode_t> mSupportedLatencyModes = {AUDIO_LATENCY_MODE_LOW};
+ // default to invalid value to force first update to the audio HAL
+ audio_latency_mode_t mSetLatencyMode =
+ (audio_latency_mode_t)AUDIO_LATENCY_MODE_INVALID;
+
+ // Bluetooth Variable latency control logic is enabled or disabled for this thread
+ std::atomic_bool mBluetoothLatencyModesEnabled;
+
private:
// one-time initialization, no locks required
sp<FastMixer> mFastMixer; // non-0 if there is also a fast mixer
@@ -1546,6 +1564,11 @@
return INVALID_OPERATION;
}
+ status_t getSupportedLatencyModes(
+ std::vector<audio_latency_mode_t>* modes) override;
+
+ status_t setBluetoothVariableLatencyEnabled(bool enabled) override;
+
protected:
virtual void setMasterMono_l(bool mono) {
mMasterMono.store(mono);
@@ -1564,6 +1587,10 @@
mFastMixer->setMasterBalance(balance);
}
}
+
+ void updateHalSupportedLatencyModes_l();
+ void onHalLatencyModesChanged_l() override;
+ void setHalLatencyMode_l() override;
};
class DirectOutputThread : public PlaybackThread {
@@ -1767,8 +1794,7 @@
}
};
-class SpatializerThread : public MixerThread,
- public StreamOutHalInterfaceLatencyModeCallback {
+class SpatializerThread : public MixerThread {
public:
SpatializerThread(const sp<AudioFlinger>& audioFlinger,
AudioStreamOut* output,
@@ -1779,32 +1805,16 @@
bool hasFastMixer() const override { return false; }
- status_t createAudioPatch_l(const struct audio_patch *patch,
- audio_patch_handle_t *handle) override;
-
// RefBase
virtual void onFirstRef();
- // StreamOutHalInterfaceLatencyModeCallback
- void onRecommendedLatencyModeChanged(std::vector<audio_latency_mode_t> modes) override;
-
status_t setRequestedLatencyMode(audio_latency_mode_t mode) override;
- status_t getSupportedLatencyModes(std::vector<audio_latency_mode_t>* modes) override;
protected:
void checkOutputStageEffects() override;
- void onHalLatencyModesChanged_l() override;
void setHalLatencyMode_l() override;
private:
- void updateHalSupportedLatencyModes_l();
-
- // Support low latency mode by default as unless explicitly indicated by the audio HAL
- // we assume the audio path is compatible with the head tracking latency requirements
- std::vector<audio_latency_mode_t> mSupportedLatencyModes = {AUDIO_LATENCY_MODE_LOW};
- // default to invalid value to force first update to the audio HAL
- audio_latency_mode_t mSetLatencyMode =
- (audio_latency_mode_t)AUDIO_LATENCY_MODE_INVALID;
// Do not request a specific mode by default
audio_latency_mode_t mRequestedLatencyMode = AUDIO_LATENCY_MODE_FREE;
diff --git a/services/audioflinger/sounddose/SoundDoseManager.h b/services/audioflinger/sounddose/SoundDoseManager.h
index b10874f..db87ad8 100644
--- a/services/audioflinger/sounddose/SoundDoseManager.h
+++ b/services/audioflinger/sounddose/SoundDoseManager.h
@@ -17,7 +17,7 @@
#pragma once
-#include <aidl/android/hardware/audio/core/ISoundDose.h>
+#include <aidl/android/hardware/audio/core/sounddose/ISoundDose.h>
#include <aidl/android/media/audio/common/AudioDevice.h>
#include <android/media/BnSoundDose.h>
#include <android/media/ISoundDoseCallback.h>
@@ -30,7 +30,7 @@
namespace android {
-using aidl::android::hardware::audio::core::ISoundDose;
+using aidl::android::hardware::audio::core::sounddose::ISoundDose;
class SoundDoseManager : public audio_utils::MelProcessor::MelCallback {
public:
diff --git a/services/audioflinger/sounddose/tests/sounddosemanager_tests.cpp b/services/audioflinger/sounddose/tests/sounddosemanager_tests.cpp
index ba2edcf..4b1edf3 100644
--- a/services/audioflinger/sounddose/tests/sounddosemanager_tests.cpp
+++ b/services/audioflinger/sounddose/tests/sounddosemanager_tests.cpp
@@ -17,7 +17,7 @@
// #define LOG_NDEBUG 0
#define LOG_TAG "SoundDoseManager_tests"
-#include <aidl/android/hardware/audio/core/BnSoundDose.h>
+#include <aidl/android/hardware/audio/core/sounddose/BnSoundDose.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -26,7 +26,7 @@
namespace android {
namespace {
-using aidl::android::hardware::audio::core::BnSoundDose;
+using aidl::android::hardware::audio::core::sounddose::BnSoundDose;
using aidl::android::media::audio::common::AudioDevice;
using aidl::android::media::audio::common::AudioDeviceAddress;
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index 520bad2..8f9c60b 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -300,6 +300,8 @@
virtual bool isUltrasoundSupported() = 0;
+ virtual bool isHotwordStreamSupported(bool lookbackAudio) = 0;
+
virtual status_t getHwOffloadFormatsSupportedForBluetoothMedia(
audio_devices_t device, std::vector<audio_format_t> *formats) = 0;
@@ -323,8 +325,11 @@
const AudioDeviceTypeAddrVector &devices) = 0;
virtual status_t removeDevicesRoleForStrategy(product_strategy_t strategy,
- device_role_t role) = 0;
+ device_role_t role,
+ const AudioDeviceTypeAddrVector &devices) = 0;
+ virtual status_t clearDevicesRoleForStrategy(product_strategy_t strategy,
+ device_role_t role) = 0;
virtual status_t getDevicesForRoleAndStrategy(product_strategy_t strategy,
device_role_t role,
diff --git a/services/audiopolicy/engine/common/include/EngineBase.h b/services/audiopolicy/engine/common/include/EngineBase.h
index 7d21ae0..8cfa592 100644
--- a/services/audiopolicy/engine/common/include/EngineBase.h
+++ b/services/audiopolicy/engine/common/include/EngineBase.h
@@ -105,7 +105,10 @@
status_t setDevicesRoleForStrategy(product_strategy_t strategy, device_role_t role,
const AudioDeviceTypeAddrVector &devices) override;
- status_t removeDevicesRoleForStrategy(product_strategy_t strategy, device_role_t role) override;
+ status_t removeDevicesRoleForStrategy(product_strategy_t strategy, device_role_t role,
+ const AudioDeviceTypeAddrVector &devices) override;
+
+ status_t clearDevicesRoleForStrategy(product_strategy_t strategy, device_role_t role) override;
status_t getDevicesForRoleAndStrategy(product_strategy_t strategy, device_role_t role,
AudioDeviceTypeAddrVector &devices) const override;
diff --git a/services/audiopolicy/engine/common/src/EngineBase.cpp b/services/audiopolicy/engine/common/src/EngineBase.cpp
index 9b78758..8015ae0 100644
--- a/services/audiopolicy/engine/common/src/EngineBase.cpp
+++ b/services/audiopolicy/engine/common/src/EngineBase.cpp
@@ -369,13 +369,11 @@
}
switch (role) {
- case DEVICE_ROLE_PREFERRED:
- case DEVICE_ROLE_DISABLED: {
+ case DEVICE_ROLE_PREFERRED: {
tDevicesRoleMap[std::make_pair(t, role)] = devices;
// The preferred devices and disabled devices are mutually exclusive. Once a device is added
// the a list, it must be removed from the other one.
- const device_role_t roleToRemove = role == DEVICE_ROLE_PREFERRED ? DEVICE_ROLE_DISABLED
- : DEVICE_ROLE_PREFERRED;
+ const device_role_t roleToRemove = DEVICE_ROLE_DISABLED;
auto it = tDevicesRoleMap.find(std::make_pair(t, roleToRemove));
if (it != tDevicesRoleMap.end()) {
it->second = excludeDeviceTypeAddrsFrom(it->second, devices);
@@ -384,6 +382,25 @@
}
}
} break;
+ case DEVICE_ROLE_DISABLED: {
+ auto it = tDevicesRoleMap.find(std::make_pair(t, role));
+ if (it != tDevicesRoleMap.end()) {
+ it->second = joinDeviceTypeAddrs(it->second, devices);
+ } else {
+ tDevicesRoleMap[std::make_pair(t, role)] = devices;
+ }
+
+ // The preferred devices and disabled devices are mutually exclusive. Once a device is added
+ // the a list, it must be removed from the other one.
+ const device_role_t roleToRemove = DEVICE_ROLE_PREFERRED;
+ it = tDevicesRoleMap.find(std::make_pair(t, roleToRemove));
+ if (it != tDevicesRoleMap.end()) {
+ it->second = excludeDeviceTypeAddrsFrom(it->second, devices);
+ if (it->second.empty()) {
+ tDevicesRoleMap.erase(it);
+ }
+ }
+ } break;
case DEVICE_ROLE_NONE:
// Intentionally fall-through as it is no need to set device role as none for a strategy.
default:
@@ -394,6 +411,36 @@
}
template <typename T>
+status_t removeDevicesRoleForT(
+ std::map<std::pair<T, device_role_t>, AudioDeviceTypeAddrVector>& tDevicesRoleMap,
+ T t, device_role_t role, const AudioDeviceTypeAddrVector &devices,
+ const std::string& logStr, std::function<bool(T)> p) {
+ if (!p(t)) {
+ ALOGE("%s invalid %s %u", __func__, logStr.c_str(), t);
+ return BAD_VALUE;
+ }
+
+ switch (role) {
+ case DEVICE_ROLE_PREFERRED:
+ case DEVICE_ROLE_DISABLED: {
+ auto it = tDevicesRoleMap.find(std::make_pair(t, role));
+ if (it != tDevicesRoleMap.end()) {
+ it->second = excludeDeviceTypeAddrsFrom(it->second, devices);
+ if (it->second.empty()) {
+ tDevicesRoleMap.erase(it);
+ }
+ }
+ } break;
+ case DEVICE_ROLE_NONE:
+ // Intentionally fall-through as it is not needed to set device role as none for a strategy.
+ default:
+ ALOGE("%s invalid role %d", __func__, role);
+ return BAD_VALUE;
+ }
+ return NO_ERROR;
+}
+
+template <typename T>
status_t removeAllDevicesRoleForT(
std::map<std::pair<T, device_role_t>, AudioDeviceTypeAddrVector>& tDevicesRoleMap,
T t, device_role_t role, const std::string& logStr, std::function<bool(T)> p) {
@@ -462,7 +509,18 @@
mProductStrategyDeviceRoleMap, strategy, role, devices, "strategy" /*logStr*/, p);
}
-status_t EngineBase::removeDevicesRoleForStrategy(product_strategy_t strategy, device_role_t role)
+status_t EngineBase::removeDevicesRoleForStrategy(product_strategy_t strategy, device_role_t role,
+ const AudioDeviceTypeAddrVector &devices)
+{
+ std::function<bool(product_strategy_t)> p = [this](product_strategy_t strategy) {
+ return mProductStrategies.find(strategy) != mProductStrategies.end();
+ };
+ return removeDevicesRoleForT(
+ mProductStrategyDeviceRoleMap, strategy, role, devices, "strategy" /*logStr*/, p);
+}
+
+status_t EngineBase::clearDevicesRoleForStrategy(product_strategy_t strategy,
+ device_role_t role)
{
std::function<bool(product_strategy_t)> p = [this](product_strategy_t strategy) {
return mProductStrategies.find(strategy) != mProductStrategies.end();
diff --git a/services/audiopolicy/engine/interface/EngineInterface.h b/services/audiopolicy/engine/interface/EngineInterface.h
index 70d25fc..b8e35ed 100644
--- a/services/audiopolicy/engine/interface/EngineInterface.h
+++ b/services/audiopolicy/engine/interface/EngineInterface.h
@@ -326,10 +326,22 @@
* for the given strategy
* @param strategy the audio strategy whose routing will be affected
* @param role the role of the devices for strategy
+ * @param devices the audio devices to be removed
* @return BAD_VALUE if the strategy or role is invalid,
* or NO_ERROR if the devices for this role was removed
*/
- virtual status_t removeDevicesRoleForStrategy(product_strategy_t strategy,
+ virtual status_t removeDevicesRoleForStrategy(product_strategy_t strategy, device_role_t role,
+ const AudioDeviceTypeAddrVector &devices) = 0;
+
+ /**
+ * @brief clearDevicesRoleForStrategy removes the role of all devices previously set
+ * for the given strategy
+ * @param strategy the audio strategy whose routing will be affected
+ * @param role the role of the devices for strategy
+ * @return BAD_VALUE if the strategy or role is invalid,
+ * or NO_ERROR if the devices for this role was removed
+ */
+ virtual status_t clearDevicesRoleForStrategy(product_strategy_t strategy,
device_role_t role) = 0;
/**
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index f99f76e..0f2b34e 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -34,6 +34,7 @@
#include <map>
#include <math.h>
#include <set>
+#include <type_traits>
#include <unordered_set>
#include <vector>
@@ -3875,14 +3876,47 @@
}
}
-status_t AudioPolicyManager::removeDevicesRoleForStrategy(product_strategy_t strategy,
- device_role_t role)
+status_t
+AudioPolicyManager::removeDevicesRoleForStrategy(product_strategy_t strategy,
+ device_role_t role,
+ const AudioDeviceTypeAddrVector &devices) {
+ ALOGV("%s() strategy=%d role=%d %s", __func__, strategy, role,
+ dumpAudioDeviceTypeAddrVector(devices).c_str());
+
+ if (!areAllDevicesSupported(devices, audio_is_output_device, __func__)) {
+ return BAD_VALUE;
+ }
+ status_t status = mEngine->removeDevicesRoleForStrategy(strategy, role, devices);
+ if (status != NO_ERROR) {
+ ALOGW("Engine could not remove devices %s for strategy %d role %d",
+ dumpAudioDeviceTypeAddrVector(devices).c_str(), strategy, role);
+ return status;
+ }
+
+ checkForDeviceAndOutputChanges();
+
+ bool forceVolumeReeval = false;
+ // TODO(b/263479999): workaround for truncated touch sounds
+ // to be removed when the problem is handled by system UI
+ uint32_t delayMs = 0;
+ if (strategy == mCommunnicationStrategy) {
+ forceVolumeReeval = true;
+ delayMs = TOUCH_SOUND_FIXED_DELAY_MS;
+ updateInputRouting();
+ }
+ updateCallAndOutputRouting(forceVolumeReeval, delayMs);
+
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManager::clearDevicesRoleForStrategy(product_strategy_t strategy,
+ device_role_t role)
{
ALOGV("%s() strategy=%d role=%d", __func__, strategy, role);
- status_t status = mEngine->removeDevicesRoleForStrategy(strategy, role);
+ status_t status = mEngine->clearDevicesRoleForStrategy(strategy, role);
if (status != NO_ERROR) {
- ALOGV("Engine could not remove preferred device for strategy %d status %d",
+ ALOGV("Engine could not remove device role for strategy %d status %d",
strategy, status);
return status;
}
@@ -5653,6 +5687,21 @@
return false;
}
+bool AudioPolicyManager::isHotwordStreamSupported(bool lookbackAudio)
+{
+ const auto mask = AUDIO_INPUT_FLAG_HOTWORD_TAP |
+ (lookbackAudio ? AUDIO_INPUT_FLAG_HW_LOOKBACK : 0);
+ for (const auto& hwModule : mHwModules) {
+ const InputProfileCollection &inputProfiles = hwModule->getInputProfiles();
+ for (const auto &inputProfile : inputProfiles) {
+ if ((inputProfile->getFlags() & mask) == mask) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
bool AudioPolicyManager::isCallScreenModeSupported()
{
return getConfig().isCallScreenModeSupported();
@@ -7406,8 +7455,11 @@
// TODO: perhaps isCompatibleProfile should return a "matching" score so we can return
// the best matching profile, not the first one.
- const audio_input_flags_t mustMatchFlag = AUDIO_INPUT_FLAG_MMAP_NOIRQ;
- const audio_input_flags_t oriFlags = flags;
+ using underlying_input_flag_t = std::underlying_type_t<audio_input_flags_t>;
+ const underlying_input_flag_t mustMatchFlag = AUDIO_INPUT_FLAG_MMAP_NOIRQ |
+ AUDIO_INPUT_FLAG_HOTWORD_TAP | AUDIO_INPUT_FLAG_HW_LOOKBACK;
+
+ const underlying_input_flag_t oriFlags = flags;
for (;;) {
sp<IOProfile> firstInexact = nullptr;
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 885f7c6..3bbcf69 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -299,8 +299,11 @@
const AudioDeviceTypeAddrVector &devices);
virtual status_t removeDevicesRoleForStrategy(product_strategy_t strategy,
- device_role_t role);
+ device_role_t role,
+ const AudioDeviceTypeAddrVector &devices);
+ virtual status_t clearDevicesRoleForStrategy(product_strategy_t strategy,
+ device_role_t role);
virtual status_t getDevicesForRoleAndStrategy(product_strategy_t strategy,
device_role_t role,
@@ -352,6 +355,8 @@
virtual bool isUltrasoundSupported();
+ bool isHotwordStreamSupported(bool lookbackAudio) override;
+
virtual status_t listAudioProductStrategies(AudioProductStrategyVector &strategies)
{
return mEngine->listAudioProductStrategies(strategies);
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 5c32209..1774600 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -667,7 +667,9 @@
return binderStatusFromStatusT(PERMISSION_DENIED);
}
- if (((flags & AUDIO_INPUT_FLAG_HW_HOTWORD) != 0)
+ if (((flags & (AUDIO_INPUT_FLAG_HW_HOTWORD |
+ AUDIO_INPUT_FLAG_HOTWORD_TAP |
+ AUDIO_INPUT_FLAG_HW_LOOKBACK)) != 0)
&& !canCaptureHotword) {
ALOGE("%s: permission denied: hotword mode not allowed"
" for uid %d pid %d", __func__, attributionSource.uid, attributionSource.pid);
@@ -2043,6 +2045,17 @@
return Status::ok();
}
+Status AudioPolicyService::isHotwordStreamSupported(bool lookbackAudio, bool* _aidl_return)
+{
+ if (mAudioPolicyManager == nullptr) {
+ return binderStatusFromStatusT(NO_INIT);
+ }
+ Mutex::Autolock _l(mLock);
+ AutoCallerClear acc;
+ *_aidl_return = mAudioPolicyManager->isHotwordStreamSupported(lookbackAudio);
+ return Status::ok();
+}
+
Status AudioPolicyService::listAudioProductStrategies(
std::vector<media::AudioProductStrategy>* _aidl_return) {
AudioProductStrategyVector strategies;
@@ -2154,8 +2167,31 @@
return binderStatusFromStatusT(status);
}
-Status AudioPolicyService::removeDevicesRoleForStrategy(int32_t strategyAidl,
- media::DeviceRole roleAidl) {
+Status AudioPolicyService::removeDevicesRoleForStrategy(
+ int32_t strategyAidl,
+ media::DeviceRole roleAidl,
+ const std::vector<AudioDevice>& devicesAidl) {
+ product_strategy_t strategy = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_product_strategy_t(strategyAidl));
+ device_role_t role = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_DeviceRole_device_role_t(roleAidl));
+ AudioDeviceTypeAddrVector devices = VALUE_OR_RETURN_BINDER_STATUS(
+ convertContainer<AudioDeviceTypeAddrVector>(devicesAidl,
+ aidl2legacy_AudioDeviceTypeAddress));
+
+ if (mAudioPolicyManager == NULL) {
+ return binderStatusFromStatusT(NO_INIT);
+ }
+ Mutex::Autolock _l(mLock);
+ status_t status = mAudioPolicyManager->removeDevicesRoleForStrategy(strategy, role, devices);
+ if (status == NO_ERROR) {
+ onCheckSpatializer_l();
+ }
+ return binderStatusFromStatusT(status);
+}
+
+Status AudioPolicyService::clearDevicesRoleForStrategy(int32_t strategyAidl,
+ media::DeviceRole roleAidl) {
product_strategy_t strategy = VALUE_OR_RETURN_BINDER_STATUS(
aidl2legacy_int32_t_product_strategy_t(strategyAidl));
device_role_t role = VALUE_OR_RETURN_BINDER_STATUS(
@@ -2164,7 +2200,7 @@
return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
- status_t status = mAudioPolicyManager->removeDevicesRoleForStrategy(strategy, role);
+ status_t status = mAudioPolicyManager->clearDevicesRoleForStrategy(strategy, role);
if (status == NO_ERROR) {
onCheckSpatializer_l();
}
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 2be5121..974ae38 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -137,6 +137,7 @@
BINDER_METHOD_ENTRY(setCurrentImeUid) \
BINDER_METHOD_ENTRY(isHapticPlaybackSupported) \
BINDER_METHOD_ENTRY(isUltrasoundSupported) \
+BINDER_METHOD_ENTRY(isHotwordStreamSupported) \
BINDER_METHOD_ENTRY(listAudioProductStrategies) \
BINDER_METHOD_ENTRY(getProductStrategyFromAudioAttributes) \
BINDER_METHOD_ENTRY(listAudioVolumeGroups) \
@@ -145,6 +146,7 @@
BINDER_METHOD_ENTRY(isCallScreenModeSupported) \
BINDER_METHOD_ENTRY(setDevicesRoleForStrategy) \
BINDER_METHOD_ENTRY(removeDevicesRoleForStrategy) \
+BINDER_METHOD_ENTRY(clearDevicesRoleForStrategy) \
BINDER_METHOD_ENTRY(getDevicesForRoleAndStrategy) \
BINDER_METHOD_ENTRY(setDevicesRoleForCapturePreset) \
BINDER_METHOD_ENTRY(addDevicesRoleForCapturePreset) \
@@ -1313,11 +1315,13 @@
case TRANSACTION_getVolumeGroupFromAudioAttributes:
case TRANSACTION_acquireSoundTriggerSession:
case TRANSACTION_releaseSoundTriggerSession:
+ case TRANSACTION_isHotwordStreamSupported:
case TRANSACTION_setRttEnabled:
case TRANSACTION_isCallScreenModeSupported:
case TRANSACTION_setDevicesRoleForStrategy:
case TRANSACTION_setSupportedSystemUsages:
case TRANSACTION_removeDevicesRoleForStrategy:
+ case TRANSACTION_clearDevicesRoleForStrategy:
case TRANSACTION_getDevicesForRoleAndStrategy:
case TRANSACTION_getDevicesForAttributes:
case TRANSACTION_setAllowedCapturePolicy:
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index 50f2180..31d5249 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -70,7 +70,7 @@
public IBinder::DeathRecipient,
public SpatializerPolicyCallback
{
- friend class BinderService<AudioPolicyService>;
+ friend class sp<AudioPolicyService>;
public:
// for BinderService
@@ -223,6 +223,7 @@
binder::Status setCurrentImeUid(int32_t uid) override;
binder::Status isHapticPlaybackSupported(bool* _aidl_return) override;
binder::Status isUltrasoundSupported(bool* _aidl_return) override;
+ binder::Status isHotwordStreamSupported(bool lookbackAudio, bool* _aidl_return) override;
binder::Status listAudioProductStrategies(
std::vector<media::AudioProductStrategy>* _aidl_return) override;
binder::Status getProductStrategyFromAudioAttributes(const media::AudioAttributesInternal& aa,
@@ -238,7 +239,12 @@
binder::Status setDevicesRoleForStrategy(
int32_t strategy, media::DeviceRole role,
const std::vector<AudioDevice>& devices) override;
- binder::Status removeDevicesRoleForStrategy(int32_t strategy, media::DeviceRole role) override;
+ binder::Status removeDevicesRoleForStrategy(
+ int32_t strategy, media::DeviceRole role,
+ const std::vector<AudioDevice>& devices) override;
+ binder::Status clearDevicesRoleForStrategy(
+ int32_t strategy,
+ media::DeviceRole role) override;
binder::Status getDevicesForRoleAndStrategy(
int32_t strategy, media::DeviceRole role,
std::vector<AudioDevice>* _aidl_return) override;
diff --git a/services/camera/libcameraservice/api2/JpegRCompositeStream.cpp b/services/camera/libcameraservice/api2/JpegRCompositeStream.cpp
index fb8979d..9857fd8 100644
--- a/services/camera/libcameraservice/api2/JpegRCompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/JpegRCompositeStream.cpp
@@ -285,22 +285,37 @@
}
size_t actualJpegRSize = 0;
- if (mSupportInternalJpeg) {
- recoverymap::jpegr_uncompressed_struct p010;
- recoverymap::jpegr_compressed_struct jpeg;
- recoverymap::jpegr_compressed_struct jpegR;
+ recoverymap::jpegr_uncompressed_struct p010;
+ recoverymap::jpegr_compressed_struct jpegR;
+ recoverymap::RecoveryMap recoveryMap;
- p010.height = inputFrame.p010Buffer.height;
- p010.width = inputFrame.p010Buffer.width;
- p010.colorGamut = recoverymap::jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
- size_t yChannelSizeInByte = p010.width * p010.height * 2;
- size_t uvChannelSizeInByte = p010.width * p010.height;
- p010.data = new uint8_t[yChannelSizeInByte + uvChannelSizeInByte];
- std::unique_ptr<uint8_t[]> p010_data;
- p010_data.reset(reinterpret_cast<uint8_t*>(p010.data));
- memcpy((uint8_t*)p010.data, inputFrame.p010Buffer.data, yChannelSizeInByte);
- memcpy((uint8_t*)p010.data + yChannelSizeInByte, inputFrame.p010Buffer.dataCb,
- uvChannelSizeInByte);
+ p010.height = inputFrame.p010Buffer.height;
+ p010.width = inputFrame.p010Buffer.width;
+ p010.colorGamut = recoverymap::jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
+ size_t yChannelSizeInByte = p010.width * p010.height * 2;
+ size_t uvChannelSizeInByte = p010.width * p010.height;
+ p010.data = new uint8_t[yChannelSizeInByte + uvChannelSizeInByte];
+ std::unique_ptr<uint8_t[]> p010_data;
+ p010_data.reset(reinterpret_cast<uint8_t*>(p010.data));
+ memcpy((uint8_t*)p010.data, inputFrame.p010Buffer.data, yChannelSizeInByte);
+ memcpy((uint8_t*)p010.data + yChannelSizeInByte, inputFrame.p010Buffer.dataCb,
+ uvChannelSizeInByte);
+
+ jpegR.data = dstBuffer;
+ jpegR.maxLength = maxJpegRBufferSize;
+
+ recoverymap::jpegr_transfer_function transferFunction;
+ switch (mP010DynamicRange) {
+ case ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10:
+ case ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10_PLUS:
+ transferFunction = recoverymap::jpegr_transfer_function::JPEGR_TF_PQ;
+ break;
+ default:
+ transferFunction = recoverymap::jpegr_transfer_function::JPEGR_TF_HLG;
+ }
+
+ if (mSupportInternalJpeg) {
+ recoverymap::jpegr_compressed_struct jpeg;
jpeg.data = inputFrame.jpegBuffer.data;
jpeg.length = android::camera2::JpegProcessor::findJpegSize(inputFrame.jpegBuffer.data,
@@ -317,28 +332,7 @@
jpeg.colorGamut = recoverymap::jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
}
- recoverymap::jpegr_transfer_function transferFunction;
- switch (mP010DynamicRange) {
- case ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10:
- case ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10_PLUS:
- transferFunction = recoverymap::jpegr_transfer_function::JPEGR_TF_PQ;
- break;
- default:
- transferFunction = recoverymap::jpegr_transfer_function::JPEGR_TF_HLG;
- }
-
- jpegR.data = dstBuffer;
- jpegR.maxLength = maxJpegRBufferSize;
-
- recoverymap::RecoveryMap recoveryMap;
res = recoveryMap.encodeJPEGR(&p010, &jpeg, transferFunction, &jpegR);
- if (res != OK) {
- ALOGE("%s: Error trying to encode JPEG/R: %s (%d)", __FUNCTION__, strerror(-res), res);
- return res;
- }
-
- actualJpegRSize = jpegR.length;
- p010_data.release();
} else {
const uint8_t* exifBuffer = nullptr;
size_t exifBufferSize = 0;
@@ -352,8 +346,22 @@
} else {
ALOGE("%s: Unable to generate App1 buffer", __FUNCTION__);
}
+
+ recoverymap::jpegr_exif_struct exif;
+ exif.data = reinterpret_cast<void*>(const_cast<uint8_t*>(exifBuffer));
+ exif.length = exifBufferSize;
+
+ res = recoveryMap.encodeJPEGR(&p010, transferFunction, &jpegR, jpegQuality, &exif);
}
+ if (res != OK) {
+ ALOGE("%s: Error trying to encode JPEG/R: %s (%d)", __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+
+ actualJpegRSize = jpegR.length;
+ p010_data.release();
+
size_t finalJpegRSize = actualJpegRSize + sizeof(CameraBlob);
if (finalJpegRSize > maxJpegRBufferSize) {
ALOGE("%s: Final jpeg buffer not large enough for the jpeg blob header", __FUNCTION__);
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index 5b8e3a1..927dc47 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -1153,14 +1153,6 @@
return OK;
}
- if (!isConcurrentDynamicRangeCaptureSupported(c,
- ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HLG10,
- ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD)) {
- // Advertise Jpeg/R only in case 10 and 8-bit concurrent capture is supported.
- // This can be removed when 10-bit to 8-bit tonemapping is available.
- return OK;
- }
-
getSupportedSizes(c, scalerSizesTag,
static_cast<android_pixel_format_t>(HAL_PIXEL_FORMAT_BLOB), &supportedBlobSizes);
getSupportedSizes(c, scalerSizesTag,