diff options
author | alk3pInjection <webmaster@raspii.tech> | 2023-04-20 00:08:54 +0800 |
---|---|---|
committer | alk3pInjection <webmaster@raspii.tech> | 2023-04-20 00:08:54 +0800 |
commit | 0111525d5a8fccbe18a9c1d11e67e4ea78289790 (patch) | |
tree | a580e3f8f49aa35d8a8256aab06b3de0c9542aca /audio | |
parent | f2e9f05ae87a2a4155798e80bc86f827e020a62a (diff) | |
parent | 8053558411d145636e2bb05f443e3436dc53e4bd (diff) |
Merge tag 'LA.QSSI.13.0.r1-09800-qssi.0' into tachibana
"LA.QSSI.13.0.r1-09800-qssi.0"
Change-Id: Ic76606f2ea0ed3f21712ea081b19c5fc802404c0
Diffstat (limited to 'audio')
16 files changed, 583 insertions, 118 deletions
diff --git a/audio/common/all-versions/default/service/android.hardware.audio.service.rc b/audio/common/all-versions/default/service/android.hardware.audio.service.rc index 45fef9a936..0de4eea373 100644 --- a/audio/common/all-versions/default/service/android.hardware.audio.service.rc +++ b/audio/common/all-versions/default/service/android.hardware.audio.service.rc @@ -3,7 +3,9 @@ service vendor.audio-hal /vendor/bin/hw/android.hardware.audio.service user audioserver # media gid needed for /dev/fm (radio) and for /data/misc/media (tee) group audio camera drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct wakelock context_hub - capabilities BLOCK_SUSPEND + capabilities BLOCK_SUSPEND SYS_NICE + # setting RLIMIT_RTPRIO allows binder RT priority inheritance + rlimit rtprio 10 10 ioprio rt 4 task_profiles ProcessCapacityHigh HighPerformance onrestart restart audioserver diff --git a/audio/core/all-versions/default/Device.cpp b/audio/core/all-versions/default/Device.cpp index b954fcd6f8..d03118ae5d 100644 --- a/audio/core/all-versions/default/Device.cpp +++ b/audio/core/all-versions/default/Device.cpp @@ -30,6 +30,7 @@ #include <algorithm> #include <android/log.h> +#include <hidl/HidlTransportSupport.h> #include <mediautils/MemoryLeakTrackUtil.h> #include <memunreachable/memunreachable.h> @@ -183,6 +184,7 @@ std::tuple<Result, sp<IStreamOut>> Device::openOutputStreamCore(int32_t ioHandle if (status == OK) { streamOut = new StreamOut(this, halStream); ++mOpenedStreamsCount; + android::hardware::setMinSchedulerPolicy(streamOut, SCHED_NORMAL, ANDROID_PRIORITY_AUDIO); } status_t convertStatus = HidlUtils::audioConfigFromHal(halConfig, false /*isInput*/, suggestedConfig); @@ -220,6 +222,7 @@ std::tuple<Result, sp<IStreamIn>> Device::openInputStreamCore( if (status == OK) { streamIn = new StreamIn(this, halStream); ++mOpenedStreamsCount; + android::hardware::setMinSchedulerPolicy(streamIn, SCHED_NORMAL, ANDROID_PRIORITY_AUDIO); } status_t convertStatus = HidlUtils::audioConfigFromHal(halConfig, true /*isInput*/, suggestedConfig); diff --git a/audio/core/all-versions/default/DevicesFactory.cpp b/audio/core/all-versions/default/DevicesFactory.cpp index f44daf0b0e..011f9ac4c2 100644 --- a/audio/core/all-versions/default/DevicesFactory.cpp +++ b/audio/core/all-versions/default/DevicesFactory.cpp @@ -23,6 +23,8 @@ #include <string.h> #include <android/log.h> +#include <hidl/HidlTransportSupport.h> +#include <system/thread_defs.h> namespace android { namespace hardware { @@ -103,6 +105,7 @@ Return<void> DevicesFactory::openDevice(const char* moduleName, Callback _hidl_c int halStatus = loadAudioInterface(moduleName, &halDevice); if (halStatus == OK) { result = new DeviceShim(halDevice); + android::hardware::setMinSchedulerPolicy(result, SCHED_NORMAL, ANDROID_PRIORITY_AUDIO); retval = Result::OK; } else if (halStatus == -EINVAL) { retval = Result::NOT_INITIALIZED; diff --git a/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp b/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp index dfc238623c..505c54c1df 100644 --- a/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp +++ b/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp @@ -14,7 +14,11 @@ * limitations under the License. */ +#include <fstream> +#include <numeric> + #include <android-base/chrono_utils.h> +#include <cutils/properties.h> #include "Generators.h" @@ -517,20 +521,10 @@ class PcmOnlyConfigOutputStreamTest : public OutputStreamTest { } bool canQueryPresentationPosition() const { - auto maybeSinkAddress = - getCachedPolicyConfig().getSinkDeviceForMixPort(getDeviceName(), getMixPortName()); - // Returning 'true' when no sink is found so the test can fail later with a more clear - // problem description. - return !maybeSinkAddress.has_value() || - !xsd::isTelephonyDevice(maybeSinkAddress.value().deviceType); + return !xsd::isTelephonyDevice(address.deviceType); } void createPatchIfNeeded() { - auto maybeSinkAddress = - getCachedPolicyConfig().getSinkDeviceForMixPort(getDeviceName(), getMixPortName()); - ASSERT_TRUE(maybeSinkAddress.has_value()) - << "No sink device found for mix port " << getMixPortName() << " (module " - << getDeviceName() << ")"; if (areAudioPatchesSupported()) { AudioPortConfig source; source.base.format.value(getConfig().base.format); @@ -540,13 +534,13 @@ class PcmOnlyConfigOutputStreamTest : public OutputStreamTest { source.ext.mix().ioHandle = helper.getIoHandle(); source.ext.mix().useCase.stream({}); AudioPortConfig sink; - sink.ext.device(maybeSinkAddress.value()); + sink.ext.device(address); EXPECT_OK(getDevice()->createAudioPatch(hidl_vec<AudioPortConfig>{source}, hidl_vec<AudioPortConfig>{sink}, returnIn(res, mPatchHandle))); mHasPatch = res == Result::OK; } else { - EXPECT_OK(stream->setDevices({maybeSinkAddress.value()})); + EXPECT_OK(stream->setDevices({address})); } } @@ -556,10 +550,6 @@ class PcmOnlyConfigOutputStreamTest : public OutputStreamTest { EXPECT_OK(getDevice()->releaseAudioPatch(mPatchHandle)); mHasPatch = false; } - } else { - if (stream) { - EXPECT_OK(stream->setDevices({address})); - } } } @@ -575,16 +565,22 @@ class PcmOnlyConfigOutputStreamTest : public OutputStreamTest { // Sometimes HAL doesn't have enough information until the audio data actually gets // consumed by the hardware. bool timedOut = false; - res = Result::INVALID_STATE; - for (android::base::Timer elapsed; - res != Result::OK && !writer.hasError() && - !(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) { - usleep(kWriteDurationUs); - ASSERT_OK(stream->getPresentationPosition(returnIn(res, framesInitial, ts))); - ASSERT_RESULT(okOrInvalidState, res); + if (!firstPosition || *firstPosition == std::numeric_limits<uint64_t>::max()) { + res = Result::INVALID_STATE; + for (android::base::Timer elapsed; + res != Result::OK && !writer.hasError() && + !(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) { + usleep(kWriteDurationUs); + ASSERT_OK(stream->getPresentationPosition(returnIn(res, framesInitial, ts))); + ASSERT_RESULT(okOrInvalidState, res); + } + ASSERT_FALSE(writer.hasError()); + ASSERT_FALSE(timedOut); + } else { + // Use `firstPosition` instead of querying it from the HAL. This is used when + // `waitForPresentationPositionAdvance` is called in a loop. + framesInitial = *firstPosition; } - ASSERT_FALSE(writer.hasError()); - ASSERT_FALSE(timedOut); uint64_t frames = framesInitial; for (android::base::Timer elapsed; @@ -646,7 +642,7 @@ TEST_P(PcmOnlyConfigOutputStreamTest, PresentationPositionPreservedOnStandby) { ASSERT_OK(stream->standby()); writer.resume(); - uint64_t frames; + uint64_t frames = std::numeric_limits<uint64_t>::max(); ASSERT_NO_FATAL_FAILURE(waitForPresentationPositionAdvance(writer, &frames)); EXPECT_GT(frames, framesInitial); @@ -691,24 +687,12 @@ class PcmOnlyConfigInputStreamTest : public InputStreamTest { InputStreamTest::TearDown(); } - bool canQueryCapturePosition() const { - auto maybeSourceAddress = getCachedPolicyConfig().getSourceDeviceForMixPort( - getDeviceName(), getMixPortName()); - // Returning 'true' when no source is found so the test can fail later with a more clear - // problem description. - return !maybeSourceAddress.has_value() || - !xsd::isTelephonyDevice(maybeSourceAddress.value().deviceType); - } + bool canQueryCapturePosition() const { return !xsd::isTelephonyDevice(address.deviceType); } void createPatchIfNeeded() { - auto maybeSourceAddress = getCachedPolicyConfig().getSourceDeviceForMixPort( - getDeviceName(), getMixPortName()); - ASSERT_TRUE(maybeSourceAddress.has_value()) - << "No source device found for mix port " << getMixPortName() << " (module " - << getDeviceName() << ")"; if (areAudioPatchesSupported()) { AudioPortConfig source; - source.ext.device(maybeSourceAddress.value()); + source.ext.device(address); AudioPortConfig sink; sink.base.format.value(getConfig().base.format); sink.base.sampleRateHz.value(getConfig().base.sampleRateHz); @@ -721,7 +705,7 @@ class PcmOnlyConfigInputStreamTest : public InputStreamTest { returnIn(res, mPatchHandle))); mHasPatch = res == Result::OK; } else { - EXPECT_OK(stream->setDevices({maybeSourceAddress.value()})); + EXPECT_OK(stream->setDevices({address})); } } @@ -731,10 +715,6 @@ class PcmOnlyConfigInputStreamTest : public InputStreamTest { EXPECT_OK(getDevice()->releaseAudioPatch(mPatchHandle)); mHasPatch = false; } - } else { - if (stream) { - EXPECT_OK(stream->setDevices({address})); - } } } @@ -864,14 +844,8 @@ TEST_P(MicrophoneInfoInputStreamTest, GetActiveMicrophones) { } ASSERT_OK(res); - auto maybeSourceAddress = - getCachedPolicyConfig().getSourceDeviceForMixPort(getDeviceName(), getMixPortName()); - ASSERT_TRUE(maybeSourceAddress.has_value()) - << "No source device found for mix port " << getMixPortName() << " (module " - << getDeviceName() << ")"; - for (auto microphone : microphones) { - if (microphone.deviceAddress == maybeSourceAddress.value()) { + if (microphone.deviceAddress == address) { StreamReader reader(stream.get(), stream->getBufferSize()); ASSERT_TRUE(reader.start()); reader.pause(); // This ensures that at least one read has happened. @@ -889,3 +863,176 @@ INSTANTIATE_TEST_CASE_P(MicrophoneInfoInputStream, MicrophoneInfoInputStreamTest ::testing::ValuesIn(getBuiltinMicConfigParameters()), &DeviceConfigParameterToString); GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MicrophoneInfoInputStreamTest); + +static const std::vector<DeviceConfigParameter>& getOutputDeviceCompressedConfigParameters( + const AudioConfigBase& configToMatch) { + static const std::vector<DeviceConfigParameter> parameters = [&] { + auto allParams = getOutputDeviceConfigParameters(); + std::vector<DeviceConfigParameter> compressedParams; + std::copy_if(allParams.begin(), allParams.end(), std::back_inserter(compressedParams), + [&](auto cfg) { + if (std::get<PARAM_CONFIG>(cfg).base != configToMatch) return false; + const auto& flags = std::get<PARAM_FLAGS>(cfg); + return std::find_if(flags.begin(), flags.end(), [](const auto& flag) { + return flag == + toString(xsd::AudioInOutFlag:: + AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD); + }) != flags.end(); + }); + return compressedParams; + }(); + return parameters; +} + +class CompressedOffloadOutputStreamTest : public PcmOnlyConfigOutputStreamTest { + public: + void loadData(const std::string& fileName, std::vector<uint8_t>* data) { + std::ifstream is(fileName, std::ios::in | std::ios::binary); + ASSERT_TRUE(is.good()) << "Failed to open file " << fileName; + is.seekg(0, is.end); + data->reserve(data->size() + is.tellg()); + is.seekg(0, is.beg); + data->insert(data->end(), std::istreambuf_iterator<char>(is), + std::istreambuf_iterator<char>()); + ASSERT_TRUE(!is.fail()) << "Failed to read from file " << fileName; + } +}; + +class OffloadCallbacks : public IStreamOutCallback { + public: + Return<void> onDrainReady() override { + ALOGI("onDrainReady"); + { + std::lock_guard lg(mLock); + mOnDrainReady = true; + } + mCondVar.notify_one(); + return {}; + } + Return<void> onWriteReady() override { return {}; } + Return<void> onError() override { + ALOGW("onError"); + { + std::lock_guard lg(mLock); + mOnError = true; + } + mCondVar.notify_one(); + return {}; + } + bool waitForDrainReadyOrError() { + std::unique_lock l(mLock); + if (!mOnDrainReady && !mOnError) { + mCondVar.wait(l, [&]() { return mOnDrainReady || mOnError; }); + } + const bool success = !mOnError; + mOnDrainReady = mOnError = false; + return success; + } + + private: + std::mutex mLock; + bool mOnDrainReady = false; + bool mOnError = false; + std::condition_variable mCondVar; +}; + +TEST_P(CompressedOffloadOutputStreamTest, Mp3FormatGaplessOffload) { + doc::test("Check that compressed offload mix ports for MP3 implement gapless offload"); + const auto& flags = getOutputFlags(); + const bool isNewDeviceLaunchingOnTPlus = property_get_int32("ro.vendor.api_level", 0) >= 33; + // See test instantiation, only offload MP3 mix ports are used. + if (std::find_if(flags.begin(), flags.end(), [](const auto& flag) { + return flag == toString(xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_GAPLESS_OFFLOAD); + }) == flags.end()) { + if (isNewDeviceLaunchingOnTPlus) { + FAIL() << "New devices launching on Android T+ must support gapless offload, " + << "see VSR-4.3-001"; + } else { + GTEST_SKIP() << "Compressed offload mix port does not support gapless offload"; + } + } + std::vector<uint8_t> offloadData; + ASSERT_NO_FATAL_FAILURE(loadData("/data/local/tmp/sine882hz3s.mp3", &offloadData)); + ASSERT_FALSE(offloadData.empty()); + ASSERT_NO_FATAL_FAILURE(createPatchIfNeeded()); + const int presentationeEndPrecisionMs = 1000; + const int sampleRate = 44100; + const int significantSampleNumber = (presentationeEndPrecisionMs * sampleRate) / 1000; + const int delay = 576 + 1000; + const int padding = 756 + 1000; + const int durationMs = 3000 - 44; + auto start = std::chrono::steady_clock::now(); + auto callbacks = sp<OffloadCallbacks>::make(); + std::mutex presentationEndLock; + std::vector<float> presentationEndTimes; + // StreamWriter plays 'offloadData' in a loop, possibly using multiple calls to 'write', this + // depends on the relative sizes of 'offloadData' and the HAL buffer. Writer calls 'onDataStart' + // each time it starts writing the buffer from the beginning, and 'onDataWrap' callback each + // time it wraps around the buffer. + StreamWriter writer( + stream.get(), stream->getBufferSize(), std::move(offloadData), + [&]() /* onDataStart */ { start = std::chrono::steady_clock::now(); }, + [&]() /* onDataWrap */ { + Return<Result> ret(Result::OK); + // Decrease the volume since the test plays a loud sine wave. + ret = stream->setVolume(0.1, 0.1); + if (!ret.isOk() || ret != Result::OK) { + ALOGE("%s: setVolume failed: %s", __func__, toString(ret).c_str()); + return false; + } + ret = Parameters::set( + stream, {{AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES, std::to_string(delay)}, + {AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES, std::to_string(padding)}}); + if (!ret.isOk() || ret != Result::OK) { + ALOGE("%s: setParameters failed: %s", __func__, toString(ret).c_str()); + return false; + } + ret = stream->drain(AudioDrain::EARLY_NOTIFY); + if (!ret.isOk() || ret != Result::OK) { + ALOGE("%s: drain failed: %s", __func__, toString(ret).c_str()); + return false; + } + // FIXME: On some SoCs intermittent errors are possible, ignore them. + if (callbacks->waitForDrainReadyOrError()) { + const float duration = std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::steady_clock::now() - start) + .count(); + std::lock_guard lg(presentationEndLock); + presentationEndTimes.push_back(duration); + } + return true; + }); + ASSERT_OK(stream->setCallback(callbacks)); + ASSERT_TRUE(writer.start()); + // How many times to loop the track so that the sum of gapless delay and padding from + // the first presentation end to the last is at least 'presentationeEndPrecisionMs'. + const int playbackNumber = (int)(significantSampleNumber / ((float)delay + padding) + 1); + for (bool done = false; !done;) { + usleep(presentationeEndPrecisionMs * 1000); + { + std::lock_guard lg(presentationEndLock); + done = presentationEndTimes.size() >= playbackNumber; + } + ASSERT_FALSE(writer.hasError()) << "Recent write or drain operation has failed"; + } + const float avgDuration = + std::accumulate(presentationEndTimes.begin(), presentationEndTimes.end(), 0.0) / + presentationEndTimes.size(); + std::stringstream observedEndTimes; + std::copy(presentationEndTimes.begin(), presentationEndTimes.end(), + std::ostream_iterator<float>(observedEndTimes, ", ")); + EXPECT_NEAR(durationMs, avgDuration, presentationeEndPrecisionMs * 0.1) + << "Observed durations: " << observedEndTimes.str(); + writer.stop(); + EXPECT_OK(stream->clearCallback()); + releasePatchIfNeeded(); +} + +INSTANTIATE_TEST_CASE_P( + CompressedOffloadOutputStream, CompressedOffloadOutputStreamTest, + ::testing::ValuesIn(getOutputDeviceCompressedConfigParameters(AudioConfigBase{ + .format = xsd::toString(xsd::AudioFormat::AUDIO_FORMAT_MP3), + .sampleRateHz = 44100, + .channelMask = xsd::toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO)})), + &DeviceConfigParameterToString); +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(CompressedOffloadOutputStreamTest); diff --git a/audio/core/all-versions/vts/functional/7.0/Generators.cpp b/audio/core/all-versions/vts/functional/7.0/Generators.cpp index f936d0afbf..8b955b6c84 100644 --- a/audio/core/all-versions/vts/functional/7.0/Generators.cpp +++ b/audio/core/all-versions/vts/functional/7.0/Generators.cpp @@ -57,9 +57,6 @@ static std::vector<AudioConfig> combineAudioConfig(std::vector<xsd::AudioChannel static std::tuple<std::vector<AudioInOutFlag>, bool> generateOutFlags( const xsd::MixPorts::MixPort& mixPort) { - static const std::vector<AudioInOutFlag> offloadFlags = { - toString(xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD), - toString(xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_DIRECT)}; std::vector<AudioInOutFlag> flags; bool isOffload = false; if (mixPort.hasFlags()) { @@ -67,14 +64,10 @@ static std::tuple<std::vector<AudioInOutFlag>, bool> generateOutFlags( isOffload = std::find(xsdFlags.begin(), xsdFlags.end(), xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != xsdFlags.end(); - if (!isOffload) { - for (auto flag : xsdFlags) { - if (flag != xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_PRIMARY) { - flags.push_back(toString(flag)); - } + for (auto flag : xsdFlags) { + if (flag != xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_PRIMARY) { + flags.push_back(toString(flag)); } - } else { - flags = offloadFlags; } } return {flags, isOffload}; @@ -85,10 +78,10 @@ static AudioOffloadInfo generateOffloadInfo(const AudioConfigBase& base) { .base = base, .streamType = toString(xsd::AudioStreamType::AUDIO_STREAM_MUSIC), .usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA), - .bitRatePerSecond = 320, + .bitRatePerSecond = 192, // as in sine882hz3s.mp3 .durationMicroseconds = -1, .bitWidth = 16, - .bufferSize = 256 // arbitrary value + .bufferSize = 72000 // 3 seconds at 192 kbps, as in sine882hz3s.mp3 }; } @@ -100,11 +93,10 @@ std::vector<DeviceConfigParameter> generateOutputDeviceConfigParameters(bool one if (!module || !module->getFirstMixPorts()) break; for (const auto& mixPort : module->getFirstMixPorts()->getMixPort()) { if (mixPort.getRole() != xsd::Role::source) continue; // not an output profile - if (getCachedPolicyConfig() - .getAttachedSinkDeviceForMixPort(moduleName, mixPort.getName()) - .empty()) { - continue; // no attached device - } + const auto attachedDeviceAddress = + getCachedPolicyConfig().getDeviceAddressOfSinkDeviceAttachedToMixPort( + moduleName, mixPort.getName()); + if (!attachedDeviceAddress.has_value()) continue; auto [flags, isOffload] = generateOutFlags(mixPort); for (const auto& profile : mixPort.getProfile()) { if (!profile.hasFormat() || !profile.hasSamplingRates() || @@ -118,7 +110,8 @@ std::vector<DeviceConfigParameter> generateOutputDeviceConfigParameters(bool one if (isOffload) { config.offloadInfo.info(generateOffloadInfo(config.base)); } - result.emplace_back(device, mixPort.getName(), config, flags); + result.emplace_back(device, mixPort.getName(), attachedDeviceAddress.value(), + config, flags); if (oneProfilePerDevice) break; } if (oneProfilePerDevice) break; @@ -162,13 +155,16 @@ const std::vector<DeviceConfigParameter>& getOutputDeviceInvalidConfigParameters profile.getFormat(), static_cast<uint32_t>(profile.getSamplingRates()[0]), toString(profile.getChannelMasks()[0])}; + DeviceAddress defaultDevice = { + toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_DEFAULT), {}}; { AudioConfig config{.base = validBase}; config.base.channelMask = "random_string"; if (isOffload) { config.offloadInfo.info(generateOffloadInfo(validBase)); } - result.emplace_back(device, mixPort.getName(), config, validFlags); + result.emplace_back(device, mixPort.getName(), defaultDevice, config, + validFlags); } { AudioConfig config{.base = validBase}; @@ -176,7 +172,8 @@ const std::vector<DeviceConfigParameter>& getOutputDeviceInvalidConfigParameters if (isOffload) { config.offloadInfo.info(generateOffloadInfo(validBase)); } - result.emplace_back(device, mixPort.getName(), config, validFlags); + result.emplace_back(device, mixPort.getName(), defaultDevice, config, + validFlags); } if (generateInvalidFlags) { AudioConfig config{.base = validBase}; @@ -184,32 +181,37 @@ const std::vector<DeviceConfigParameter>& getOutputDeviceInvalidConfigParameters config.offloadInfo.info(generateOffloadInfo(validBase)); } std::vector<AudioInOutFlag> flags = {"random_string", ""}; - result.emplace_back(device, mixPort.getName(), config, flags); + result.emplace_back(device, mixPort.getName(), defaultDevice, config, + flags); } if (isOffload) { { AudioConfig config{.base = validBase}; config.offloadInfo.info(generateOffloadInfo(validBase)); config.offloadInfo.info().base.channelMask = "random_string"; - result.emplace_back(device, mixPort.getName(), config, validFlags); + result.emplace_back(device, mixPort.getName(), defaultDevice, config, + validFlags); } { AudioConfig config{.base = validBase}; config.offloadInfo.info(generateOffloadInfo(validBase)); config.offloadInfo.info().base.format = "random_string"; - result.emplace_back(device, mixPort.getName(), config, validFlags); + result.emplace_back(device, mixPort.getName(), defaultDevice, config, + validFlags); } { AudioConfig config{.base = validBase}; config.offloadInfo.info(generateOffloadInfo(validBase)); config.offloadInfo.info().streamType = "random_string"; - result.emplace_back(device, mixPort.getName(), config, validFlags); + result.emplace_back(device, mixPort.getName(), defaultDevice, config, + validFlags); } { AudioConfig config{.base = validBase}; config.offloadInfo.info(generateOffloadInfo(validBase)); config.offloadInfo.info().usage = "random_string"; - result.emplace_back(device, mixPort.getName(), config, validFlags); + result.emplace_back(device, mixPort.getName(), defaultDevice, config, + validFlags); } hasOffloadConfig = true; } else { @@ -233,11 +235,10 @@ std::vector<DeviceConfigParameter> generateInputDeviceConfigParameters(bool oneP if (!module || !module->getFirstMixPorts()) break; for (const auto& mixPort : module->getFirstMixPorts()->getMixPort()) { if (mixPort.getRole() != xsd::Role::sink) continue; // not an input profile - if (getCachedPolicyConfig() - .getAttachedSourceDeviceForMixPort(moduleName, mixPort.getName()) - .empty()) { - continue; // no attached device - } + const auto attachedDeviceAddress = + getCachedPolicyConfig().getDeviceAddressOfSourceDeviceAttachedToMixPort( + moduleName, mixPort.getName()); + if (!attachedDeviceAddress.has_value()) continue; std::vector<AudioInOutFlag> flags; if (mixPort.hasFlags()) { std::transform(mixPort.getFlags().begin(), mixPort.getFlags().end(), @@ -250,7 +251,8 @@ std::vector<DeviceConfigParameter> generateInputDeviceConfigParameters(bool oneP auto configs = combineAudioConfig(profile.getChannelMasks(), profile.getSamplingRates(), profile.getFormat()); for (const auto& config : configs) { - result.emplace_back(device, mixPort.getName(), config, flags); + result.emplace_back(device, mixPort.getName(), attachedDeviceAddress.value(), + config, flags); if (oneProfilePerDevice) break; } if (oneProfilePerDevice) break; @@ -298,20 +300,25 @@ const std::vector<DeviceConfigParameter>& getInputDeviceInvalidConfigParameters( profile.getFormat(), static_cast<uint32_t>(profile.getSamplingRates()[0]), toString(profile.getChannelMasks()[0])}; + DeviceAddress defaultDevice = { + toString(xsd::AudioDevice::AUDIO_DEVICE_IN_DEFAULT), {}}; { AudioConfig config{.base = validBase}; config.base.channelMask = "random_string"; - result.emplace_back(device, mixPort.getName(), config, validFlags); + result.emplace_back(device, mixPort.getName(), defaultDevice, config, + validFlags); } { AudioConfig config{.base = validBase}; config.base.format = "random_string"; - result.emplace_back(device, mixPort.getName(), config, validFlags); + result.emplace_back(device, mixPort.getName(), defaultDevice, config, + validFlags); } if (generateInvalidFlags) { AudioConfig config{.base = validBase}; std::vector<AudioInOutFlag> flags = {"random_string", ""}; - result.emplace_back(device, mixPort.getName(), config, flags); + result.emplace_back(device, mixPort.getName(), defaultDevice, config, + flags); } hasConfig = true; break; diff --git a/audio/core/all-versions/vts/functional/7.0/PolicyConfig.h b/audio/core/all-versions/vts/functional/7.0/PolicyConfig.h index 4aea503938..c1d5669775 100644 --- a/audio/core/all-versions/vts/functional/7.0/PolicyConfig.h +++ b/audio/core/all-versions/vts/functional/7.0/PolicyConfig.h @@ -61,6 +61,18 @@ class PolicyConfig { const std::set<std::string>& getModulesWithDevicesNames() const { return mModulesWithDevicesNames; } + std::optional<DeviceAddress> getDeviceAddressOfSinkDeviceAttachedToMixPort( + const std::string& moduleName, const std::string& mixPortName) const { + const auto attachedDevicePort = getAttachedSinkDeviceForMixPort(moduleName, mixPortName); + if (attachedDevicePort.empty()) return {}; + return getDeviceAddressOfDevicePort(moduleName, attachedDevicePort); + } + std::optional<DeviceAddress> getDeviceAddressOfSourceDeviceAttachedToMixPort( + const std::string& moduleName, const std::string& mixPortName) const { + const auto attachedDevicePort = getAttachedSourceDeviceForMixPort(moduleName, mixPortName); + if (attachedDevicePort.empty()) return {}; + return getDeviceAddressOfDevicePort(moduleName, attachedDevicePort); + } std::string getAttachedSinkDeviceForMixPort(const std::string& moduleName, const std::string& mixPortName) const { return findAttachedDevice(getAttachedDevices(moduleName), @@ -84,8 +96,6 @@ class PolicyConfig { const std::vector<std::string>& getAttachedDevices(const std::string& moduleName) const; std::optional<DeviceAddress> getDeviceAddressOfDevicePort( const std::string& moduleName, const std::string& devicePortName) const; - std::string getDevicePortTagNameFromType(const std::string& moduleName, - const AudioDevice& deviceType) const; std::set<std::string> getSinkDevicesForMixPort(const std::string& moduleName, const std::string& mixPortName) const; std::set<std::string> getSourceDevicesForMixPort(const std::string& moduleName, diff --git a/audio/core/all-versions/vts/functional/Android.bp b/audio/core/all-versions/vts/functional/Android.bp index c757032e0f..daed7a8afd 100644 --- a/audio/core/all-versions/vts/functional/Android.bp +++ b/audio/core/all-versions/vts/functional/Android.bp @@ -34,6 +34,7 @@ cc_defaults { ], shared_libs: [ "libbinder", + "libcutils", "libfmq", "libxml2", ], @@ -190,6 +191,7 @@ cc_test { ], data: [ ":audio_policy_configuration_V7_0", + "data/sine882hz3s.mp3", ], // Use test_config for vts suite. // TODO(b/146104851): Add auto-gen rules and remove it. @@ -223,6 +225,7 @@ cc_test { ], data: [ ":audio_policy_configuration_V7_1", + "data/sine882hz3s.mp3", ], // Use test_config for vts suite. // TODO(b/146104851): Add auto-gen rules and remove it. diff --git a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h index 38e9e5f467..e46e5b4f52 100644 --- a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h +++ b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h @@ -617,7 +617,8 @@ static std::string DeviceConfigParameterToString( std::get<PARAM_FLAGS>(info.param))); #elif MAJOR_VERSION >= 7 const auto configPart = - std::to_string(config.base.sampleRateHz) + "_" + + ::testing::PrintToString(std::get<PARAM_ATTACHED_DEV_ADDR>(info.param).deviceType) + + "_" + std::to_string(config.base.sampleRateHz) + "_" + // The channel masks and flags are vectors of strings, just need to sanitize them. SanitizeStringForGTestName(::testing::PrintToString(config.base.channelMask)) + "_" + SanitizeStringForGTestName(::testing::PrintToString(std::get<PARAM_FLAGS>(info.param))); @@ -658,6 +659,9 @@ class AudioHidlTestWithDeviceConfigParameter std::get<INDEX_OUTPUT>(std::get<PARAM_FLAGS>(GetParam()))); } #elif MAJOR_VERSION >= 7 + DeviceAddress getAttachedDeviceAddress() const { + return std::get<PARAM_ATTACHED_DEV_ADDR>(GetParam()); + } hidl_vec<AudioInOutFlag> getInputFlags() const { return std::get<PARAM_FLAGS>(GetParam()); } hidl_vec<AudioInOutFlag> getOutputFlags() const { return std::get<PARAM_FLAGS>(GetParam()); } #endif @@ -933,6 +937,15 @@ class StreamWriter : public StreamWorker<StreamWriter> { StreamWriter(IStreamOut* stream, size_t bufferSize) : mStream(stream), mBufferSize(bufferSize), mData(mBufferSize) {} + StreamWriter(IStreamOut* stream, size_t bufferSize, std::vector<uint8_t>&& data, + std::function<void()> onDataStart, std::function<bool()> onDataWrap) + : mStream(stream), + mBufferSize(bufferSize), + mData(std::move(data)), + mOnDataStart(onDataStart), + mOnDataWrap(onDataWrap) { + ALOGI("StreamWriter data size: %d", (int)mData.size()); + } ~StreamWriter() { stop(); if (mEfGroup) { @@ -998,9 +1011,12 @@ class StreamWriter : public StreamWorker<StreamWriter> { ALOGE("command message queue write failed"); return false; } - const size_t dataSize = std::min(mData.size(), mDataMQ->availableToWrite()); - bool success = mDataMQ->write(mData.data(), dataSize); + if (mDataPosition == 0) mOnDataStart(); + const size_t dataSize = std::min(mData.size() - mDataPosition, mDataMQ->availableToWrite()); + bool success = mDataMQ->write(mData.data() + mDataPosition, dataSize); ALOGE_IF(!success, "data message queue write failed"); + mDataPosition += dataSize; + if (mDataPosition >= mData.size()) mDataPosition = 0; mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY)); uint32_t efState = 0; @@ -1026,6 +1042,9 @@ class StreamWriter : public StreamWorker<StreamWriter> { ALOGE("bad wait status: %d", ret); success = false; } + if (success && mDataPosition == 0) { + success = mOnDataWrap(); + } return success; } @@ -1033,6 +1052,9 @@ class StreamWriter : public StreamWorker<StreamWriter> { IStreamOut* const mStream; const size_t mBufferSize; std::vector<uint8_t> mData; + std::function<void()> mOnDataStart = []() {}; + std::function<bool()> mOnDataWrap = []() { return true; }; + size_t mDataPosition = 0; std::unique_ptr<CommandMQ> mCommandMQ; std::unique_ptr<DataMQ> mDataMQ; std::unique_ptr<StatusMQ> mStatusMQ; @@ -1047,7 +1069,7 @@ class OutputStreamTest #if MAJOR_VERSION <= 6 address.device = AudioDevice::OUT_DEFAULT; #elif MAJOR_VERSION >= 7 - address.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_DEFAULT); + address = getAttachedDeviceAddress(); #endif const AudioConfig& config = getConfig(); auto flags = getOutputFlags(); @@ -1243,16 +1265,11 @@ class InputStreamTest #if MAJOR_VERSION <= 6 address.device = AudioDevice::IN_DEFAULT; #elif MAJOR_VERSION >= 7 - auto maybeSourceAddress = getCachedPolicyConfig().getSourceDeviceForMixPort( - getDeviceName(), getMixPortName()); + address = getAttachedDeviceAddress(); auto& metadata = initMetadata.tracks[0]; - if (maybeSourceAddress.has_value() && - !xsd::isTelephonyDevice(maybeSourceAddress.value().deviceType)) { - address = maybeSourceAddress.value(); + if (!xsd::isTelephonyDevice(address.deviceType)) { metadata.source = toString(xsd::AudioSource::AUDIO_SOURCE_UNPROCESSED); metadata.channelMask = getConfig().base.channelMask; - } else { - address.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_IN_DEFAULT); } #if MAJOR_VERSION == 7 && MINOR_VERSION >= 1 auto flagsIt = std::find(flags.begin(), flags.end(), diff --git a/audio/core/all-versions/vts/functional/AudioTestDefinitions.h b/audio/core/all-versions/vts/functional/AudioTestDefinitions.h index 802b87bc36..3de06c35ac 100644 --- a/audio/core/all-versions/vts/functional/AudioTestDefinitions.h +++ b/audio/core/all-versions/vts/functional/AudioTestDefinitions.h @@ -39,9 +39,10 @@ using DeviceConfigParameter = std::tuple< std::variant<android::hardware::audio::common::COMMON_TYPES_CPP_VERSION::AudioInputFlag, android::hardware::audio::common::COMMON_TYPES_CPP_VERSION::AudioOutputFlag>>; #elif MAJOR_VERSION >= 7 -enum { PARAM_DEVICE, PARAM_PORT_NAME, PARAM_CONFIG, PARAM_FLAGS }; +enum { PARAM_DEVICE, PARAM_PORT_NAME, PARAM_ATTACHED_DEV_ADDR, PARAM_CONFIG, PARAM_FLAGS }; using DeviceConfigParameter = std::tuple<DeviceParameter, std::string, + android::hardware::audio::common::COMMON_TYPES_CPP_VERSION::DeviceAddress, android::hardware::audio::common::COMMON_TYPES_CPP_VERSION::AudioConfig, std::vector<android::hardware::audio::CORE_TYPES_CPP_VERSION::AudioInOutFlag>>; #endif diff --git a/audio/core/all-versions/vts/functional/VtsHalAudioV7_0TargetTest.xml b/audio/core/all-versions/vts/functional/VtsHalAudioV7_0TargetTest.xml index f0e26958b9..8da5744089 100644 --- a/audio/core/all-versions/vts/functional/VtsHalAudioV7_0TargetTest.xml +++ b/audio/core/all-versions/vts/functional/VtsHalAudioV7_0TargetTest.xml @@ -29,6 +29,7 @@ <option name="cleanup" value="true" /> <option name="push" value="VtsHalAudioV7_0TargetTest->/data/local/tmp/VtsHalAudioV7_0TargetTest" /> <option name="push" value="audio_policy_configuration_V7_0.xsd->/data/local/tmp/audio_policy_configuration_V7_0.xsd" /> + <option name="push" value="sine882hz3s.mp3->/data/local/tmp/sine882hz3s.mp3" /> </target_preparer> <test class="com.android.tradefed.testtype.GTest" > diff --git a/audio/core/all-versions/vts/functional/VtsHalAudioV7_1TargetTest.xml b/audio/core/all-versions/vts/functional/VtsHalAudioV7_1TargetTest.xml index 7ce1477e1a..227df1803d 100644 --- a/audio/core/all-versions/vts/functional/VtsHalAudioV7_1TargetTest.xml +++ b/audio/core/all-versions/vts/functional/VtsHalAudioV7_1TargetTest.xml @@ -29,6 +29,8 @@ <option name="cleanup" value="true" /> <option name="push" value="VtsHalAudioV7_1TargetTest->/data/local/tmp/VtsHalAudioV7_1TargetTest" /> <option name="push" value="audio_policy_configuration_V7_1.xsd->/data/local/tmp/audio_policy_configuration_V7_1.xsd" /> + <option name="push" value="sine882hz3s.mp3->/data/local/tmp/sine882hz3s.mp3" /> + </target_preparer> <test class="com.android.tradefed.testtype.GTest" > diff --git a/audio/core/all-versions/vts/functional/data/sine882hz3s.mp3 b/audio/core/all-versions/vts/functional/data/sine882hz3s.mp3 Binary files differnew file mode 100644 index 0000000000..0604f9b3b4 --- /dev/null +++ b/audio/core/all-versions/vts/functional/data/sine882hz3s.mp3 diff --git a/audio/effect/all-versions/default/Effect.cpp b/audio/effect/all-versions/default/Effect.cpp index 3baafc9343..87e1ab7a7d 100644 --- a/audio/effect/all-versions/default/Effect.cpp +++ b/audio/effect/all-versions/default/Effect.cpp @@ -25,8 +25,11 @@ #define ATRACE_TAG ATRACE_TAG_AUDIO #include <HidlUtils.h> #include <android/log.h> +#include <cutils/properties.h> #include <media/EffectsFactoryApi.h> #include <mediautils/ScopedStatistics.h> +#include <sys/syscall.h> +#include <system/audio_effects/effect_spatializer.h> #include <util/EffectUtils.h> #include <utils/Trace.h> @@ -47,6 +50,160 @@ using ::android::hardware::audio::common::COMMON_TYPES_CPP_VERSION::implementati namespace { +/** + * Some basic scheduling tools. + */ +namespace scheduler { + +int getCpu() { + return sched_getcpu(); +} + +uint64_t getAffinity(pid_t tid) { + cpu_set_t set; + CPU_ZERO_S(sizeof(set), &set); + + if (sched_getaffinity(tid, sizeof(set), &set)) { + ALOGW("%s: for tid:%d returning 0, failed %s", __func__, tid, strerror(errno)); + return 0; + } + const int count = CPU_COUNT_S(sizeof(set), &set); + uint64_t mask = 0; + for (int i = 0; i < CPU_SETSIZE; ++i) { + if (CPU_ISSET_S(i, sizeof(set), &set)) { + mask |= 1 << i; + } + } + ALOGV("%s: for tid:%d returning cpu count %d mask %llu", __func__, tid, count, + (unsigned long long)mask); + return mask; +} + +status_t setAffinity(pid_t tid, uint64_t mask) { + cpu_set_t set; + CPU_ZERO_S(sizeof(set), &set); + + for (uint64_t m = mask; m != 0;) { + uint64_t tz = __builtin_ctz(m); + CPU_SET_S(tz, sizeof(set), &set); + m &= ~(1 << tz); + } + if (sched_setaffinity(tid, sizeof(set), &set)) { + ALOGW("%s: for tid:%d setting cpu mask %llu failed %s", __func__, tid, + (unsigned long long)mask, strerror(errno)); + return -errno; + } + ALOGV("%s: for tid:%d setting cpu mask %llu", __func__, tid, (unsigned long long)mask); + return OK; +} + +__unused status_t setPriority(pid_t tid, int policy, int priority) { + struct sched_param param { + .sched_priority = priority, + }; + if (sched_setscheduler(tid, policy, ¶m) != 0) { + ALOGW("%s: Cannot set FIFO priority for tid %d to policy %d priority %d %s", __func__, tid, + policy, priority, strerror(errno)); + return -errno; + } + ALOGV("%s: Successfully set priority for tid %d to policy %d priority %d", __func__, tid, + policy, priority); + return NO_ERROR; +} + +status_t setUtilMin(pid_t tid, uint32_t utilMin) { + // Currently, there is no wrapper in bionic: b/183240349. + struct { + uint32_t size; + uint32_t sched_policy; + uint64_t sched_flags; + int32_t sched_nice; + uint32_t sched_priority; + uint64_t sched_runtime; + uint64_t sched_deadline; + uint64_t sched_period; + uint32_t sched_util_min; + uint32_t sched_util_max; + } attr{ + .size = sizeof(attr), + .sched_flags = SCHED_FLAG_KEEP_ALL | SCHED_FLAG_UTIL_CLAMP_MIN, + .sched_util_min = utilMin, + }; + + if (syscall(__NR_sched_setattr, tid, &attr, 0 /* flags */)) { + ALOGW("%s: Cannot set sched_util_min for pid %d to %u %s", __func__, tid, utilMin, + strerror(errno)); + return -errno; + } + ALOGV("%s: Successfully set sched_util_min for pid %d to %u", __func__, tid, utilMin); + return NO_ERROR; +} + +/* + Attempts to raise the priority and usage of tid for spatialization. + Returns OK if everything works. +*/ +status_t updateSpatializerPriority(pid_t tid) { + status_t status = OK; + + const int cpu = getCpu(); + ALOGV("%s: current CPU:%d", __func__, cpu); + + const auto currentAffinity = getAffinity(tid); + ALOGV("%s: current Affinity:%llx", __func__, (unsigned long long)currentAffinity); + + // Set the desired CPU core affinity. + // Typically this would be done to move the Spatializer effect off of the little cores. + // The mid cores and large cores typically have more FP/NEON units + // and will advantageously reduce power and prevent glitches due CPU limitations. + // + // Since this is SOC dependent, we do not set the core affinity here but + // prefer to set the util_clamp_min below. + // + constexpr uint64_t kDefaultAffinity = 0; + const int32_t desiredAffinity = + property_get_int32("audio.spatializer.effect.affinity", kDefaultAffinity); + if (desiredAffinity != 0 && (desiredAffinity & ~currentAffinity) == 0) { + const status_t localStatus = setAffinity(tid, desiredAffinity); + status = status ? status : localStatus; + } + + // Set the util_clamp_min. + // This is beneficial to reduce glitches when starting up, or due to scheduler + // thread statistics reset (e.g. core migration), which cause the CPU frequency to drop + // to minimum. + // + // Experimentation has found that moving to a mid core over a little core reduces + // power if the mid core (e.g. A76/78) has more (e.g. 2x) FP/NEON units + // than the little core (e.g. A55). + // A possible value is 300. + // + constexpr uint32_t kUtilMin = 0; + const int32_t utilMin = property_get_int32("audio.spatializer.effect.util_clamp_min", kUtilMin); + if (utilMin > 0 && utilMin <= 1024) { + const status_t localStatus = setUtilMin(tid, utilMin); + status = status ? status : localStatus; + } + +#if 0 + // Provided for local vendor testing but not enabled as audioserver does this for us. + // + // Set priority if specified. + constexpr int32_t kRTPriorityMin = 1; + constexpr int32_t kRTPriorityMax = 3; + const int32_t priorityBoost = + property_get_int32("audio.spatializer.priority", kRTPriorityMin); + if (priorityBoost >= kRTPriorityMin && priorityBoost <= kRTPriorityMax) { + const status_t localStatus = scheduler::setPriority(threadId, SCHED_FIFO, priorityBoost); + status = status ? status : localStatus; + } +#endif + + return status; +} + +} // namespace scheduler + #define SCOPED_STATS() \ ::android::mediautils::ScopedStatistics scopedStatistics { \ std::string("EffectHal::").append(__func__), mEffectHal->mStatistics \ @@ -83,6 +240,16 @@ class ProcessThread : public Thread { }; bool ProcessThread::threadLoop() { + // For a spatializer effect, we perform scheduler adjustments to reduce glitches and power. + { + effect_descriptor_t halDescriptor{}; + if ((*mEffect)->get_descriptor(mEffect, &halDescriptor) == NO_ERROR && + memcmp(&halDescriptor.type, FX_IID_SPATIALIZER, sizeof(effect_uuid_t)) == 0) { + const status_t status = scheduler::updateSpatializerPriority(gettid()); + ALOGW_IF(status != OK, "Failed to update Spatializer priority"); + } + } + // This implementation doesn't return control back to the Thread until it decides to stop, // as the Thread uses mutexes, and this can lead to priority inversion. while (!std::atomic_load_explicit(mStop, std::memory_order_acquire)) { @@ -238,12 +405,27 @@ void Effect::effectOffloadParamToHal(const EffectOffloadParameter& offload, } // static -std::vector<uint8_t> Effect::parameterToHal(uint32_t paramSize, const void* paramData, - uint32_t valueSize, const void** valueData) { +bool Effect::parameterToHal(uint32_t paramSize, const void* paramData, uint32_t valueSize, + const void** valueData, std::vector<uint8_t>* halParamBuffer) { + constexpr size_t kMaxSize = EFFECT_PARAM_SIZE_MAX - sizeof(effect_param_t); + if (paramSize > kMaxSize) { + ALOGE("%s: Parameter size is too big: %" PRIu32, __func__, paramSize); + return false; + } size_t valueOffsetFromData = alignedSizeIn<uint32_t>(paramSize) * sizeof(uint32_t); + if (valueOffsetFromData > kMaxSize) { + ALOGE("%s: Aligned parameter size is too big: %zu", __func__, valueOffsetFromData); + return false; + } + if (valueSize > kMaxSize - valueOffsetFromData) { + ALOGE("%s: Value size is too big: %" PRIu32 ", max size is %zu", __func__, valueSize, + kMaxSize - valueOffsetFromData); + android_errorWriteLog(0x534e4554, "237291425"); + return false; + } size_t halParamBufferSize = sizeof(effect_param_t) + valueOffsetFromData + valueSize; - std::vector<uint8_t> halParamBuffer(halParamBufferSize, 0); - effect_param_t* halParam = reinterpret_cast<effect_param_t*>(&halParamBuffer[0]); + halParamBuffer->resize(halParamBufferSize, 0); + effect_param_t* halParam = reinterpret_cast<effect_param_t*>(halParamBuffer->data()); halParam->psize = paramSize; halParam->vsize = valueSize; memcpy(halParam->data, paramData, paramSize); @@ -256,7 +438,7 @@ std::vector<uint8_t> Effect::parameterToHal(uint32_t paramSize, const void* para *valueData = halParam->data + valueOffsetFromData; } } - return halParamBuffer; + return true; } Result Effect::analyzeCommandStatus(const char* commandName, const char* context, status_t status) { @@ -301,6 +483,11 @@ void Effect::getConfigImpl(int commandCode, const char* commandName, GetConfigCa Result Effect::getCurrentConfigImpl(uint32_t featureId, uint32_t configSize, GetCurrentConfigSuccessCallback onSuccess) { + if (configSize > kMaxDataSize - sizeof(uint32_t)) { + ALOGE("%s: Config size is too big: %" PRIu32, __func__, configSize); + android_errorWriteLog(0x534e4554, "240266798"); + return Result::INVALID_ARGUMENTS; + } uint32_t halCmd = featureId; std::vector<uint32_t> halResult(alignedSizeIn<uint32_t>(sizeof(uint32_t) + configSize), 0); uint32_t halResultSize = 0; @@ -314,11 +501,15 @@ Result Effect::getParameterImpl(uint32_t paramSize, const void* paramData, GetParameterSuccessCallback onSuccess) { // As it is unknown what method HAL uses for copying the provided parameter data, // it is safer to make sure that input and output buffers do not overlap. - std::vector<uint8_t> halCmdBuffer = - parameterToHal(paramSize, paramData, requestValueSize, nullptr); + std::vector<uint8_t> halCmdBuffer; + if (!parameterToHal(paramSize, paramData, requestValueSize, nullptr, &halCmdBuffer)) { + return Result::INVALID_ARGUMENTS; + } const void* valueData = nullptr; - std::vector<uint8_t> halParamBuffer = - parameterToHal(paramSize, paramData, replyValueSize, &valueData); + std::vector<uint8_t> halParamBuffer; + if (!parameterToHal(paramSize, paramData, replyValueSize, &valueData, &halParamBuffer)) { + return Result::INVALID_ARGUMENTS; + } uint32_t halParamBufferSize = halParamBuffer.size(); return sendCommandReturningStatusAndData( @@ -331,8 +522,12 @@ Result Effect::getParameterImpl(uint32_t paramSize, const void* paramData, Result Effect::getSupportedConfigsImpl(uint32_t featureId, uint32_t maxConfigs, uint32_t configSize, GetSupportedConfigsSuccessCallback onSuccess) { + if (maxConfigs != 0 && configSize > (kMaxDataSize - 2 * sizeof(uint32_t)) / maxConfigs) { + ALOGE("%s: Config size is too big: %" PRIu32, __func__, configSize); + return Result::INVALID_ARGUMENTS; + } uint32_t halCmd[2] = {featureId, maxConfigs}; - uint32_t halResultSize = 2 * sizeof(uint32_t) + maxConfigs * sizeof(configSize); + uint32_t halResultSize = 2 * sizeof(uint32_t) + maxConfigs * configSize; std::vector<uint8_t> halResult(static_cast<size_t>(halResultSize), 0); return sendCommandReturningStatusAndData( EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS, "GET_FEATURE_SUPPORTED_CONFIGS", sizeof(halCmd), @@ -472,8 +667,10 @@ Result Effect::setConfigImpl(int commandCode, const char* commandName, const Eff Result Effect::setParameterImpl(uint32_t paramSize, const void* paramData, uint32_t valueSize, const void* valueData) { - std::vector<uint8_t> halParamBuffer = - parameterToHal(paramSize, paramData, valueSize, &valueData); + std::vector<uint8_t> halParamBuffer; + if (!parameterToHal(paramSize, paramData, valueSize, &valueData, &halParamBuffer)) { + return Result::INVALID_ARGUMENTS; + } return sendCommandReturningStatus(EFFECT_CMD_SET_PARAM, "SET_PARAM", halParamBuffer.size(), &halParamBuffer[0]); } @@ -670,8 +867,21 @@ Return<void> Effect::command(uint32_t commandId, const hidl_vec<uint8_t>& data, void* dataPtr = halDataSize > 0 ? &halData[0] : NULL; void* resultPtr = halResultSize > 0 ? &halResult[0] : NULL; - status_t status = - (*mHandle)->command(mHandle, commandId, halDataSize, dataPtr, &halResultSize, resultPtr); + status_t status = BAD_VALUE; + switch (commandId) { + case 'gtid': // retrieve the tid, used for spatializer priority boost + if (halDataSize == 0 && resultMaxSize == sizeof(int32_t)) { + auto ptid = (int32_t*)resultPtr; + ptid[0] = mProcessThread ? mProcessThread->getTid() : -1; + status = OK; + break; // we have handled 'gtid' here. + } + [[fallthrough]]; // allow 'gtid' overload (checked halDataSize and resultMaxSize). + default: + status = (*mHandle)->command(mHandle, commandId, halDataSize, dataPtr, &halResultSize, + resultPtr); + break; + } hidl_vec<uint8_t> result; if (status == OK && resultPtr != NULL) { result.setToExternal(&halResult[0], halResultSize); diff --git a/audio/effect/all-versions/default/Effect.h b/audio/effect/all-versions/default/Effect.h index 011544d760..5d8dcccba6 100644 --- a/audio/effect/all-versions/default/Effect.h +++ b/audio/effect/all-versions/default/Effect.h @@ -184,6 +184,9 @@ struct Effect : public IEffect { using GetSupportedConfigsSuccessCallback = std::function<void(uint32_t supportedConfigs, void* configsData)>; + // Sets the limit on the maximum size of vendor-provided data structures. + static constexpr size_t kMaxDataSize = 1 << 20; + static const char* sContextResultOfCommand; static const char* sContextCallToCommand; static const char* sContextCallFunction; @@ -211,8 +214,8 @@ struct Effect : public IEffect { channel_config_t* halConfig); static void effectOffloadParamToHal(const EffectOffloadParameter& offload, effect_offload_param_t* halOffload); - static std::vector<uint8_t> parameterToHal(uint32_t paramSize, const void* paramData, - uint32_t valueSize, const void** valueData); + static bool parameterToHal(uint32_t paramSize, const void* paramData, uint32_t valueSize, + const void** valueData, std::vector<uint8_t>* halParamBuffer); Result analyzeCommandStatus(const char* commandName, const char* context, status_t status); void getConfigImpl(int commandCode, const char* commandName, GetConfigCallback cb); diff --git a/audio/effect/all-versions/default/EffectsFactory.cpp b/audio/effect/all-versions/default/EffectsFactory.cpp index e93ad893a8..9bf309c224 100644 --- a/audio/effect/all-versions/default/EffectsFactory.cpp +++ b/audio/effect/all-versions/default/EffectsFactory.cpp @@ -32,6 +32,7 @@ #include <UuidUtils.h> #include <android/log.h> +#include <hidl/HidlTransportSupport.h> #include <media/EffectsFactoryApi.h> #include <system/audio_effects/effect_aec.h> #include <system/audio_effects/effect_agc.h> @@ -44,6 +45,7 @@ #include <system/audio_effects/effect_presetreverb.h> #include <system/audio_effects/effect_virtualizer.h> #include <system/audio_effects/effect_visualizer.h> +#include <system/thread_defs.h> #include <util/EffectUtils.h> namespace android { @@ -189,6 +191,7 @@ Return<void> EffectsFactory::createEffectImpl(const Uuid& uuid, int32_t session, status = (*handle)->get_descriptor(handle, &halDescriptor); if (status == OK) { effect = dispatchEffectInstanceCreation(halDescriptor, handle); + android::hardware::setMinSchedulerPolicy(effect, SCHED_NORMAL, ANDROID_PRIORITY_AUDIO); effectId = EffectMap::getInstance().add(handle); } else { ALOGE("Error querying effect descriptor for %s: %s", diff --git a/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp b/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp index e59423fa98..d95bb06c3a 100644 --- a/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp +++ b/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp @@ -35,6 +35,7 @@ #include <common/all-versions/VersionUtils.h> +#include <cutils/properties.h> #include <gtest/gtest.h> #include <hidl/GtestPrinter.h> #include <hidl/ServiceManagement.h> @@ -623,6 +624,27 @@ TEST_P(AudioEffectHidlTest, GetParameter) { EXPECT_TRUE(ret.isOk()); } +TEST_P(AudioEffectHidlTest, GetParameterInvalidMaxReplySize) { + description("Verify that GetParameter caps the maximum reply size"); + const bool isNewDeviceLaunchingOnTPlus = property_get_int32("ro.vendor.api_level", 0) >= 33; + if (!isNewDeviceLaunchingOnTPlus) { + GTEST_SKIP() << "The test only applies to devices launching on T or later"; + } + // Use a non-empty parameter to avoid being rejected by any earlier checks. + hidl_vec<uint8_t> parameter; + parameter.resize(16); + // Use very large size to ensure that the service does not crash. Since parameters + // are specific to each effect, and some effects may not have parameters at all, + // simply checking the return value would not reveal an issue of using an uncapped value. + const uint32_t veryLargeReplySize = std::numeric_limits<uint32_t>::max() - 100; + Result retval = Result::OK; + Return<void> ret = + effect->getParameter(parameter, veryLargeReplySize, + [&](Result r, const hidl_vec<uint8_t>&) { retval = r; }); + EXPECT_TRUE(ret.isOk()); + EXPECT_EQ(Result::INVALID_ARGUMENTS, retval); +} + TEST_P(AudioEffectHidlTest, GetSupportedConfigsForFeature) { description("Verify that GetSupportedConfigsForFeature does not crash"); Return<void> ret = effect->getSupportedConfigsForFeature( @@ -643,6 +665,37 @@ TEST_P(AudioEffectHidlTest, SetCurrentConfigForFeature) { EXPECT_TRUE(ret.isOk()); } +TEST_P(AudioEffectHidlTest, GetSupportedConfigsForFeatureInvalidConfigSize) { + description("Verify that GetSupportedConfigsForFeature caps the maximum config size"); + const bool isNewDeviceLaunchingOnTPlus = property_get_int32("ro.vendor.api_level", 0) >= 33; + if (!isNewDeviceLaunchingOnTPlus) { + GTEST_SKIP() << "The test only applies to devices launching on T or later"; + } + // Use very large size to ensure that the service does not crash. + const uint32_t veryLargeConfigSize = std::numeric_limits<uint32_t>::max() - 100; + Result retval = Result::OK; + Return<void> ret = effect->getSupportedConfigsForFeature( + 0, 1, veryLargeConfigSize, + [&](Result r, uint32_t, const hidl_vec<uint8_t>&) { retval = r; }); + EXPECT_TRUE(ret.isOk()); + EXPECT_EQ(Result::INVALID_ARGUMENTS, retval); +} + +TEST_P(AudioEffectHidlTest, GetCurrentConfigForFeatureInvalidConfigSize) { + description("Verify that GetCurrentConfigForFeature caps the maximum config size"); + const bool isNewDeviceLaunchingOnTPlus = property_get_int32("ro.vendor.api_level", 0) >= 33; + if (!isNewDeviceLaunchingOnTPlus) { + GTEST_SKIP() << "The test only applies to devices launching on T or later"; + } + // Use very large size to ensure that the service does not crash. + const uint32_t veryLargeConfigSize = std::numeric_limits<uint32_t>::max() - 100; + Result retval = Result::OK; + Return<void> ret = effect->getCurrentConfigForFeature( + 0, veryLargeConfigSize, [&](Result r, const hidl_vec<uint8_t>&) { retval = r; }); + EXPECT_TRUE(ret.isOk()); + EXPECT_EQ(Result::INVALID_ARGUMENTS, retval); +} + // The main test class for Equalizer Audio Effect HIDL HAL. class EqualizerAudioEffectHidlTest : public AudioEffectHidlTest { public: |