summaryrefslogtreecommitdiff
path: root/audio/core/all-versions/default/StreamIn.cpp
diff options
context:
space:
mode:
authorKevin Rocard <krocard@google.com>2018-11-14 16:22:07 -0800
committerKevin Rocard <krocard@google.com>2018-11-28 20:29:52 -0800
commit96d2cd9d5c87d8bf9766a7190cf358adccab349a (patch)
treeeb67244d62b8278f994da71e40a4e49cfc2b2788 /audio/core/all-versions/default/StreamIn.cpp
parent6166ef40f9e24436e1d727050c99fc5b185a9e17 (diff)
Audio HAL: Factorize V2 and V4 like libaudiohal
Use the same technique developed for the simpler libaudiohal to factorize the audio HAL impl. Generated with script: set -euo pipefail cd hardware/interfaces/audio # Make 2.0 and 4.0 files mostly identical by removing all 2.0, V4.0... # and replacing it by CPP_VERSION and FILE_VERSION # This allow both implementation to be mostly identical, except for code difference # Also remove IncludeGuard.h as it will be included from compiler flag # Also update license to 2018 find -name *.cpp -o -name *h | grep -v -e service.cpp | xargs sed -Ei \ -e 's/(#.*)V[42]_0_(.*_H_?)$/\1\2/' \ -e 's/(LOG_TAG.*)[24]_0/\1/' \ -e '/#ifndef AUDIO_HAL_VERSION/,/#endif/d' \ -e '/^#.*def.*AUDIO_HAL_VERSION\b/d' \ -e's/V[42]_0/CPP_VERSION/' \ -e 's/(#include ).(android.*)[24].0(.*).$/\1PATH(\2FILE_VERSION\3)/' \ -e 's|(#include) .(.*)[24].0/(default/.*).$|\1 "\2\3"|' \ -e 's/\bAUDIO_HAL_VERSION\b/CPP_VERSION/' \ -e '/IncludeGuard.h/d' \ -e 's/(Copyright .C. 201)./\18/' # set MAJOR_VERSION instead of having a different macro for each version # this allow to do preprocessor arithmetic (eg: MAJOR_VERSION >= 4) for v in 2 4; do find -ipath '*/'$v'.0/*/Android.bp' | xargs sed -i -e '/cflags:/,/^$/d' -e '/vndk/{:a;/}/!{N;ba};d}' -e '$i\ cflags: [\ "-DMAJOR_VERSION='$v'",\ "-DMINOR_VERSION=0",\ "-include common/all-versions/VersionMacro.h",\ ]'; done # replace# ifdef VERSION_X by #if MAJOR_VERSION == X find -path *all-versions/*.h | xargs sed -Ei -e 's/def.*AUDIO_HAL_VERSION_([24])_0.*/ MAJOR_VERSION == \1/' \ -e 'T;s/ +/ /' # copy all-versions impl in each version impl find -path '*/[24].0/*.[hc]*' | grep -ve all-versions -e test | xargs -P99 -n4 sed -i -Ee '/include <.*all-versions.default/!b' \ -e 's#.*<#find -path */#' -e 's/>$/|xargs tail -n +16/' -e e # remove all-versions impl rm -r {core,effect}/all-versions/ # merge version impl into a single all-version for dir in core/*.0/vts/functional/*.0/ ;do dest=$(echo $dir | sed 's#/..0/#/all-versions/#') mkdir -p $dest mv -T $dir $dest done find -mindepth 3 -path '*/2.0/*' -a \( -name '*cpp' -o -name '*h' \) | grep -v 'all-versions' | sed -E 'h;s/2/4/g;H;s/4.0/all-versions/;s/4.0//;H;g;s/\n/ /g;'| xargs -P99 -L1 sh -c ' set -euo pipefail mkdir -p $(dirname $2); diff --old-group-format="#if MAJOR_VERSION == 2 %<#endif " \ --new-group-format="#if MAJOR_VERSION == 4 %>#endif " $0 $1 > $2 || true; rm $0 $1' # merge the X.0 Android.bp & OWNERS in the all-versions for dir in common effect core; do for sub in default vts/functional; do test -f $dir/2.0/$sub/Android.bp || continue awk 1 $dir/*.0/$sub/Android.bp >> $dir/all-versions/$sub/Android.bp # delete licenses except for the first one and add whitelines sed -i -e '/^}$/{N;/^}\n$/!s/\n/&\n/}' $dir/all-versions/$sub/Android.bp sed -i -e '1,17b;/^\/\//,/^$/{d}' $dir/all-versions/$sub/Android.bp done for sub in default vts; do test -d $dir/2.0/$sub || continue test -f $dir/2.0/$sub/OWNERS && awk 1 $dir/*.0/$sub/OWNERS | sort -u > $dir/all-versions/$sub/OWNERS || true rm -r $dir/*.0/$sub done done # delete all-versions-impl dependencies find -name 'Android.bp' | xargs sed -i -e '/all-versions-impl/d' # cleanup unused files rm common/all-versions/default/include/common/all-versions/default/HidlUtils* rm common/all-versions/util/include/common/all-versions/IncludeGuard.h find -depth -type d -empty -delete # Clamp consecutive number of empty lines to 2 find -name *.cpp -o -name *h | xargs sed -Ei ':a;/^\n*$/{N;ba};s/\n\n+/\n\n/' # transform #endif\n#if to #elif find -name *.cpp -o -name *h | xargs sed -i '/^#endif/{N;s/.*\n#if/#elif/}' # remove leftover include guard in cpp find -name *.cpp |xargs sed -Ei '/^#.*_H_?$/d' # apply clang-format find -name *cpp -o -name *h | xargs ../../../prebuilts/clang/host/linux-x86/clang-stable/bin/clang-format --style file -i # clang format breaks PATH(a/b) to PATH(a / b), remove the space surrounding / find -name *cpp -o -name *h | xargs sed -i "/#include PATH/s# / #/#g" Test: compile Bug: 118203066 Change-Id: I3692a444307afc5f71064fe0b9e6b8af3c9ff1dd Signed-off-by: Kevin Rocard <krocard@google.com>
Diffstat (limited to 'audio/core/all-versions/default/StreamIn.cpp')
-rw-r--r--audio/core/all-versions/default/StreamIn.cpp495
1 files changed, 495 insertions, 0 deletions
diff --git a/audio/core/all-versions/default/StreamIn.cpp b/audio/core/all-versions/default/StreamIn.cpp
new file mode 100644
index 0000000000..d19526a1dd
--- /dev/null
+++ b/audio/core/all-versions/default/StreamIn.cpp
@@ -0,0 +1,495 @@
+/*
+ * Copyright (C) 2018 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 "StreamInHAL"
+
+#include "core/default/StreamIn.h"
+#include "core/default/Conversions.h"
+#include "core/default/Util.h"
+
+//#define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_AUDIO
+
+#include <android/log.h>
+#include <hardware/audio.h>
+#include <utils/Trace.h>
+#include <memory>
+
+using ::android::hardware::audio::common::CPP_VERSION::ThreadInfo;
+using ::android::hardware::audio::CPP_VERSION::MessageQueueFlagBits;
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace CPP_VERSION {
+namespace implementation {
+
+namespace {
+
+class ReadThread : public Thread {
+ public:
+ // ReadThread's lifespan never exceeds StreamIn's lifespan.
+ ReadThread(std::atomic<bool>* stop, audio_stream_in_t* stream, StreamIn::CommandMQ* commandMQ,
+ StreamIn::DataMQ* dataMQ, StreamIn::StatusMQ* statusMQ, EventFlag* efGroup)
+ : Thread(false /*canCallJava*/),
+ mStop(stop),
+ mStream(stream),
+ mCommandMQ(commandMQ),
+ mDataMQ(dataMQ),
+ mStatusMQ(statusMQ),
+ mEfGroup(efGroup),
+ mBuffer(nullptr) {}
+ bool init() {
+ mBuffer.reset(new (std::nothrow) uint8_t[mDataMQ->getQuantumCount()]);
+ return mBuffer != nullptr;
+ }
+ virtual ~ReadThread() {}
+
+ private:
+ std::atomic<bool>* mStop;
+ audio_stream_in_t* mStream;
+ StreamIn::CommandMQ* mCommandMQ;
+ StreamIn::DataMQ* mDataMQ;
+ StreamIn::StatusMQ* mStatusMQ;
+ EventFlag* mEfGroup;
+ std::unique_ptr<uint8_t[]> mBuffer;
+ IStreamIn::ReadParameters mParameters;
+ IStreamIn::ReadStatus mStatus;
+
+ bool threadLoop() override;
+
+ void doGetCapturePosition();
+ void doRead();
+};
+
+void ReadThread::doRead() {
+ size_t availableToWrite = mDataMQ->availableToWrite();
+ size_t requestedToRead = mParameters.params.read;
+ if (requestedToRead > availableToWrite) {
+ ALOGW(
+ "truncating read data from %d to %d due to insufficient data queue "
+ "space",
+ (int32_t)requestedToRead, (int32_t)availableToWrite);
+ requestedToRead = availableToWrite;
+ }
+ ssize_t readResult = mStream->read(mStream, &mBuffer[0], requestedToRead);
+ mStatus.retval = Result::OK;
+ if (readResult >= 0) {
+ mStatus.reply.read = readResult;
+ if (!mDataMQ->write(&mBuffer[0], readResult)) {
+ ALOGW("data message queue write failed");
+ }
+ } else {
+ mStatus.retval = Stream::analyzeStatus("read", readResult);
+ }
+}
+
+void ReadThread::doGetCapturePosition() {
+ mStatus.retval = StreamIn::getCapturePositionImpl(
+ mStream, &mStatus.reply.capturePosition.frames, &mStatus.reply.capturePosition.time);
+}
+
+bool ReadThread::threadLoop() {
+ // 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)) {
+ uint32_t efState = 0;
+ mEfGroup->wait(static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL), &efState);
+ if (!(efState & static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL))) {
+ continue; // Nothing to do.
+ }
+ if (!mCommandMQ->read(&mParameters)) {
+ continue; // Nothing to do.
+ }
+ mStatus.replyTo = mParameters.command;
+ switch (mParameters.command) {
+ case IStreamIn::ReadCommand::READ:
+ doRead();
+ break;
+ case IStreamIn::ReadCommand::GET_CAPTURE_POSITION:
+ doGetCapturePosition();
+ break;
+ default:
+ ALOGE("Unknown read thread command code %d", mParameters.command);
+ mStatus.retval = Result::NOT_SUPPORTED;
+ break;
+ }
+ if (!mStatusMQ->write(&mStatus)) {
+ ALOGW("status message queue write failed");
+ }
+ mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY));
+ }
+
+ return false;
+}
+
+} // namespace
+
+StreamIn::StreamIn(const sp<Device>& device, audio_stream_in_t* stream)
+ : mIsClosed(false),
+ mDevice(device),
+ mStream(stream),
+ mStreamCommon(new Stream(&stream->common)),
+ mStreamMmap(new StreamMmap<audio_stream_in_t>(stream)),
+ mEfGroup(nullptr),
+ mStopReadThread(false) {}
+
+StreamIn::~StreamIn() {
+ ATRACE_CALL();
+ close();
+ if (mReadThread.get()) {
+ ATRACE_NAME("mReadThread->join");
+ status_t status = mReadThread->join();
+ ALOGE_IF(status, "read thread exit error: %s", strerror(-status));
+ }
+ if (mEfGroup) {
+ status_t status = EventFlag::deleteEventFlag(&mEfGroup);
+ ALOGE_IF(status, "read MQ event flag deletion error: %s", strerror(-status));
+ }
+ mDevice->closeInputStream(mStream);
+ mStream = nullptr;
+}
+
+// Methods from ::android::hardware::audio::CPP_VERSION::IStream follow.
+Return<uint64_t> StreamIn::getFrameSize() {
+ return audio_stream_in_frame_size(mStream);
+}
+
+Return<uint64_t> StreamIn::getFrameCount() {
+ return mStreamCommon->getFrameCount();
+}
+
+Return<uint64_t> StreamIn::getBufferSize() {
+ return mStreamCommon->getBufferSize();
+}
+
+Return<uint32_t> StreamIn::getSampleRate() {
+ return mStreamCommon->getSampleRate();
+}
+
+#if MAJOR_VERSION == 2
+Return<void> StreamIn::getSupportedChannelMasks(getSupportedChannelMasks_cb _hidl_cb) {
+ return mStreamCommon->getSupportedChannelMasks(_hidl_cb);
+}
+Return<void> StreamIn::getSupportedSampleRates(getSupportedSampleRates_cb _hidl_cb) {
+ return mStreamCommon->getSupportedSampleRates(_hidl_cb);
+}
+#endif
+
+Return<void> StreamIn::getSupportedChannelMasks(AudioFormat format,
+ getSupportedChannelMasks_cb _hidl_cb) {
+ return mStreamCommon->getSupportedChannelMasks(format, _hidl_cb);
+}
+Return<void> StreamIn::getSupportedSampleRates(AudioFormat format,
+ getSupportedSampleRates_cb _hidl_cb) {
+ return mStreamCommon->getSupportedSampleRates(format, _hidl_cb);
+}
+
+Return<Result> StreamIn::setSampleRate(uint32_t sampleRateHz) {
+ return mStreamCommon->setSampleRate(sampleRateHz);
+}
+
+Return<AudioChannelBitfield> StreamIn::getChannelMask() {
+ return mStreamCommon->getChannelMask();
+}
+
+Return<Result> StreamIn::setChannelMask(AudioChannelBitfield mask) {
+ return mStreamCommon->setChannelMask(mask);
+}
+
+Return<AudioFormat> StreamIn::getFormat() {
+ return mStreamCommon->getFormat();
+}
+
+Return<void> StreamIn::getSupportedFormats(getSupportedFormats_cb _hidl_cb) {
+ return mStreamCommon->getSupportedFormats(_hidl_cb);
+}
+
+Return<Result> StreamIn::setFormat(AudioFormat format) {
+ return mStreamCommon->setFormat(format);
+}
+
+Return<void> StreamIn::getAudioProperties(getAudioProperties_cb _hidl_cb) {
+ return mStreamCommon->getAudioProperties(_hidl_cb);
+}
+
+Return<Result> StreamIn::addEffect(uint64_t effectId) {
+ return mStreamCommon->addEffect(effectId);
+}
+
+Return<Result> StreamIn::removeEffect(uint64_t effectId) {
+ return mStreamCommon->removeEffect(effectId);
+}
+
+Return<Result> StreamIn::standby() {
+ return mStreamCommon->standby();
+}
+
+Return<Result> StreamIn::setHwAvSync(uint32_t hwAvSync) {
+ return mStreamCommon->setHwAvSync(hwAvSync);
+}
+
+#if MAJOR_VERSION == 2
+Return<Result> StreamIn::setConnectedState(const DeviceAddress& address, bool connected) {
+ return mStreamCommon->setConnectedState(address, connected);
+}
+
+Return<AudioDevice> StreamIn::getDevice() {
+ return mStreamCommon->getDevice();
+}
+
+Return<Result> StreamIn::setDevice(const DeviceAddress& address) {
+ return mStreamCommon->setDevice(address);
+}
+
+Return<void> StreamIn::getParameters(const hidl_vec<hidl_string>& keys, getParameters_cb _hidl_cb) {
+ return mStreamCommon->getParameters(keys, _hidl_cb);
+}
+
+Return<Result> StreamIn::setParameters(const hidl_vec<ParameterValue>& parameters) {
+ return mStreamCommon->setParameters(parameters);
+}
+
+Return<void> StreamIn::debugDump(const hidl_handle& fd) {
+ return mStreamCommon->debugDump(fd);
+}
+#elif MAJOR_VERSION == 4
+Return<void> StreamIn::getDevices(getDevices_cb _hidl_cb) {
+ return mStreamCommon->getDevices(_hidl_cb);
+}
+
+Return<Result> StreamIn::setDevices(const hidl_vec<DeviceAddress>& devices) {
+ return mStreamCommon->setDevices(devices);
+}
+Return<void> StreamIn::getParameters(const hidl_vec<ParameterValue>& context,
+ const hidl_vec<hidl_string>& keys, getParameters_cb _hidl_cb) {
+ return mStreamCommon->getParameters(context, keys, _hidl_cb);
+}
+
+Return<Result> StreamIn::setParameters(const hidl_vec<ParameterValue>& context,
+ const hidl_vec<ParameterValue>& parameters) {
+ return mStreamCommon->setParameters(context, parameters);
+}
+#endif
+
+Return<Result> StreamIn::start() {
+ return mStreamMmap->start();
+}
+
+Return<Result> StreamIn::stop() {
+ return mStreamMmap->stop();
+}
+
+Return<void> StreamIn::createMmapBuffer(int32_t minSizeFrames, createMmapBuffer_cb _hidl_cb) {
+ return mStreamMmap->createMmapBuffer(minSizeFrames, audio_stream_in_frame_size(mStream),
+ _hidl_cb);
+}
+
+Return<void> StreamIn::getMmapPosition(getMmapPosition_cb _hidl_cb) {
+ return mStreamMmap->getMmapPosition(_hidl_cb);
+}
+
+Return<Result> StreamIn::close() {
+ if (mIsClosed) return Result::INVALID_STATE;
+ mIsClosed = true;
+ if (mReadThread.get()) {
+ mStopReadThread.store(true, std::memory_order_release);
+ }
+ if (mEfGroup) {
+ mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL));
+ }
+ return Result::OK;
+}
+
+// Methods from ::android::hardware::audio::CPP_VERSION::IStreamIn follow.
+Return<void> StreamIn::getAudioSource(getAudioSource_cb _hidl_cb) {
+ int halSource;
+ Result retval = mStreamCommon->getParam(AudioParameter::keyInputSource, &halSource);
+ AudioSource source(AudioSource::DEFAULT);
+ if (retval == Result::OK) {
+ source = AudioSource(halSource);
+ }
+ _hidl_cb(retval, source);
+ return Void();
+}
+
+Return<Result> StreamIn::setGain(float gain) {
+ if (!isGainNormalized(gain)) {
+ ALOGW("Can not set a stream input gain (%f) outside [0,1]", gain);
+ return Result::INVALID_ARGUMENTS;
+ }
+ return Stream::analyzeStatus("set_gain", mStream->set_gain(mStream, gain));
+}
+
+Return<void> StreamIn::prepareForReading(uint32_t frameSize, uint32_t framesCount,
+ prepareForReading_cb _hidl_cb) {
+ status_t status;
+ ThreadInfo threadInfo = {0, 0};
+
+ // Wrap the _hidl_cb to return an error
+ auto sendError = [&threadInfo, &_hidl_cb](Result result) {
+ _hidl_cb(result, CommandMQ::Descriptor(), DataMQ::Descriptor(), StatusMQ::Descriptor(),
+ threadInfo);
+ };
+
+ // Create message queues.
+ if (mDataMQ) {
+ ALOGE("the client attempts to call prepareForReading twice");
+ sendError(Result::INVALID_STATE);
+ return Void();
+ }
+ std::unique_ptr<CommandMQ> tempCommandMQ(new CommandMQ(1));
+
+ // Check frameSize and framesCount
+ if (frameSize == 0 || framesCount == 0) {
+ ALOGE("Null frameSize (%u) or framesCount (%u)", frameSize, framesCount);
+ sendError(Result::INVALID_ARGUMENTS);
+ return Void();
+ }
+
+ if (frameSize > Stream::MAX_BUFFER_SIZE / framesCount) {
+ ALOGE("Buffer too big: %u*%u bytes > MAX_BUFFER_SIZE (%u)", frameSize, framesCount,
+ Stream::MAX_BUFFER_SIZE);
+ sendError(Result::INVALID_ARGUMENTS);
+ return Void();
+ }
+ std::unique_ptr<DataMQ> tempDataMQ(new DataMQ(frameSize * framesCount, true /* EventFlag */));
+
+ std::unique_ptr<StatusMQ> tempStatusMQ(new StatusMQ(1));
+ if (!tempCommandMQ->isValid() || !tempDataMQ->isValid() || !tempStatusMQ->isValid()) {
+ ALOGE_IF(!tempCommandMQ->isValid(), "command MQ is invalid");
+ ALOGE_IF(!tempDataMQ->isValid(), "data MQ is invalid");
+ ALOGE_IF(!tempStatusMQ->isValid(), "status MQ is invalid");
+ sendError(Result::INVALID_ARGUMENTS);
+ return Void();
+ }
+ EventFlag* tempRawEfGroup{};
+ status = EventFlag::createEventFlag(tempDataMQ->getEventFlagWord(), &tempRawEfGroup);
+ std::unique_ptr<EventFlag, void (*)(EventFlag*)> tempElfGroup(
+ tempRawEfGroup, [](auto* ef) { EventFlag::deleteEventFlag(&ef); });
+ if (status != OK || !tempElfGroup) {
+ ALOGE("failed creating event flag for data MQ: %s", strerror(-status));
+ sendError(Result::INVALID_ARGUMENTS);
+ return Void();
+ }
+
+ // Create and launch the thread.
+ auto tempReadThread =
+ std::make_unique<ReadThread>(&mStopReadThread, mStream, tempCommandMQ.get(),
+ tempDataMQ.get(), tempStatusMQ.get(), tempElfGroup.get());
+ if (!tempReadThread->init()) {
+ ALOGW("failed to start reader thread: %s", strerror(-status));
+ sendError(Result::INVALID_ARGUMENTS);
+ return Void();
+ }
+ status = tempReadThread->run("reader", PRIORITY_URGENT_AUDIO);
+ if (status != OK) {
+ ALOGW("failed to start reader thread: %s", strerror(-status));
+ sendError(Result::INVALID_ARGUMENTS);
+ return Void();
+ }
+
+ mCommandMQ = std::move(tempCommandMQ);
+ mDataMQ = std::move(tempDataMQ);
+ mStatusMQ = std::move(tempStatusMQ);
+ mReadThread = tempReadThread.release();
+ mEfGroup = tempElfGroup.release();
+ threadInfo.pid = getpid();
+ threadInfo.tid = mReadThread->getTid();
+ _hidl_cb(Result::OK, *mCommandMQ->getDesc(), *mDataMQ->getDesc(), *mStatusMQ->getDesc(),
+ threadInfo);
+ return Void();
+}
+
+Return<uint32_t> StreamIn::getInputFramesLost() {
+ return mStream->get_input_frames_lost(mStream);
+}
+
+// static
+Result StreamIn::getCapturePositionImpl(audio_stream_in_t* stream, uint64_t* frames,
+ uint64_t* time) {
+ // HAL may have a stub function, always returning ENOSYS, don't
+ // spam the log in this case.
+ static const std::vector<int> ignoredErrors{ENOSYS};
+ Result retval(Result::NOT_SUPPORTED);
+ if (stream->get_capture_position == NULL) return retval;
+ int64_t halFrames, halTime;
+ retval = Stream::analyzeStatus("get_capture_position",
+ stream->get_capture_position(stream, &halFrames, &halTime),
+ ignoredErrors);
+ if (retval == Result::OK) {
+ *frames = halFrames;
+ *time = halTime;
+ }
+ return retval;
+};
+
+Return<void> StreamIn::getCapturePosition(getCapturePosition_cb _hidl_cb) {
+ uint64_t frames = 0, time = 0;
+ Result retval = getCapturePositionImpl(mStream, &frames, &time);
+ _hidl_cb(retval, frames, time);
+ return Void();
+}
+
+Return<void> StreamIn::debug(const hidl_handle& fd, const hidl_vec<hidl_string>& options) {
+ return mStreamCommon->debug(fd, options);
+}
+
+#if MAJOR_VERSION == 4
+Return<void> StreamIn::updateSinkMetadata(const SinkMetadata& sinkMetadata) {
+ if (mStream->update_sink_metadata == nullptr) {
+ return Void(); // not supported by the HAL
+ }
+ std::vector<record_track_metadata> halTracks;
+ halTracks.reserve(sinkMetadata.tracks.size());
+ for (auto& metadata : sinkMetadata.tracks) {
+ halTracks.push_back(
+ {.source = static_cast<audio_source_t>(metadata.source), .gain = metadata.gain});
+ }
+ const sink_metadata_t halMetadata = {
+ .track_count = halTracks.size(),
+ .tracks = halTracks.data(),
+ };
+ mStream->update_sink_metadata(mStream, &halMetadata);
+ return Void();
+}
+
+Return<void> StreamIn::getActiveMicrophones(getActiveMicrophones_cb _hidl_cb) {
+ Result retval = Result::NOT_SUPPORTED;
+ size_t actual_mics = AUDIO_MICROPHONE_MAX_COUNT;
+ audio_microphone_characteristic_t mic_array[AUDIO_MICROPHONE_MAX_COUNT];
+
+ hidl_vec<MicrophoneInfo> microphones;
+ if (mStream->get_active_microphones != NULL &&
+ mStream->get_active_microphones(mStream, &mic_array[0], &actual_mics) == 0) {
+ microphones.resize(actual_mics);
+ for (size_t i = 0; i < actual_mics; ++i) {
+ halToMicrophoneCharacteristics(&microphones[i], mic_array[i]);
+ }
+ retval = Result::OK;
+ }
+
+ _hidl_cb(retval, microphones);
+ return Void();
+}
+#endif
+
+} // namespace implementation
+} // namespace CPP_VERSION
+} // namespace audio
+} // namespace hardware
+} // namespace android