1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "StreamInHAL"
18 
19 #include "core/default/StreamIn.h"
20 #include "core/default/Conversions.h"
21 #include "core/default/Util.h"
22 #include "common/all-versions/HidlSupport.h"
23 
24 //#define LOG_NDEBUG 0
25 #define ATRACE_TAG ATRACE_TAG_AUDIO
26 
27 #include <android/log.h>
28 #include <hardware/audio.h>
29 #include <utils/Trace.h>
30 #include <memory>
31 #include <cmath>
32 
33 namespace android {
34 namespace hardware {
35 namespace audio {
36 namespace CPP_VERSION {
37 namespace implementation {
38 
39 namespace {
40 
41 class ReadThread : public Thread {
42    public:
43     // 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)44     ReadThread(std::atomic<bool>* stop, audio_stream_in_t* stream, StreamIn::CommandMQ* commandMQ,
45                StreamIn::DataMQ* dataMQ, StreamIn::StatusMQ* statusMQ, EventFlag* efGroup)
46         : Thread(false /*canCallJava*/),
47           mStop(stop),
48           mStream(stream),
49           mCommandMQ(commandMQ),
50           mDataMQ(dataMQ),
51           mStatusMQ(statusMQ),
52           mEfGroup(efGroup),
53           mBuffer(nullptr) {}
init()54     bool init() {
55         mBuffer.reset(new (std::nothrow) uint8_t[mDataMQ->getQuantumCount()]);
56         return mBuffer != nullptr;
57     }
~ReadThread()58     virtual ~ReadThread() {}
59 
60    private:
61     std::atomic<bool>* mStop;
62     audio_stream_in_t* mStream;
63     StreamIn::CommandMQ* mCommandMQ;
64     StreamIn::DataMQ* mDataMQ;
65     StreamIn::StatusMQ* mStatusMQ;
66     EventFlag* mEfGroup;
67     std::unique_ptr<uint8_t[]> mBuffer;
68     IStreamIn::ReadParameters mParameters;
69     IStreamIn::ReadStatus mStatus;
70 
71     bool threadLoop() override;
72 
73     void doGetCapturePosition();
74     void doRead();
75 };
76 
doRead()77 void ReadThread::doRead() {
78     size_t availableToWrite = mDataMQ->availableToWrite();
79     size_t requestedToRead = mParameters.params.read;
80     if (requestedToRead > availableToWrite) {
81         ALOGW(
82             "truncating read data from %d to %d due to insufficient data queue "
83             "space",
84             (int32_t)requestedToRead, (int32_t)availableToWrite);
85         requestedToRead = availableToWrite;
86     }
87     ssize_t readResult = mStream->read(mStream, &mBuffer[0], requestedToRead);
88     mStatus.retval = Result::OK;
89     if (readResult >= 0) {
90         mStatus.reply.read = readResult;
91         if (!mDataMQ->write(&mBuffer[0], readResult)) {
92             ALOGW("data message queue write failed");
93         }
94     } else {
95         mStatus.retval = Stream::analyzeStatus("read", readResult);
96     }
97 }
98 
doGetCapturePosition()99 void ReadThread::doGetCapturePosition() {
100     mStatus.retval = StreamIn::getCapturePositionImpl(
101         mStream, &mStatus.reply.capturePosition.frames, &mStatus.reply.capturePosition.time);
102 }
103 
threadLoop()104 bool ReadThread::threadLoop() {
105     // This implementation doesn't return control back to the Thread until it
106     // decides to stop,
107     // as the Thread uses mutexes, and this can lead to priority inversion.
108     while (!std::atomic_load_explicit(mStop, std::memory_order_acquire)) {
109         uint32_t efState = 0;
110         mEfGroup->wait(static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL), &efState);
111         if (!(efState & static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL))) {
112             continue;  // Nothing to do.
113         }
114         if (!mCommandMQ->read(&mParameters)) {
115             continue;  // Nothing to do.
116         }
117         mStatus.replyTo = mParameters.command;
118         switch (mParameters.command) {
119             case IStreamIn::ReadCommand::READ:
120                 doRead();
121                 break;
122             case IStreamIn::ReadCommand::GET_CAPTURE_POSITION:
123                 doGetCapturePosition();
124                 break;
125             default:
126                 ALOGE("Unknown read thread command code %d", mParameters.command);
127                 mStatus.retval = Result::NOT_SUPPORTED;
128                 break;
129         }
130         if (!mStatusMQ->write(&mStatus)) {
131             ALOGW("status message queue write failed");
132         }
133         mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY));
134     }
135 
136     return false;
137 }
138 
139 }  // namespace
140 
StreamIn(const sp<Device> & device,audio_stream_in_t * stream)141 StreamIn::StreamIn(const sp<Device>& device, audio_stream_in_t* stream)
142     : mDevice(device),
143       mStream(stream),
144       mStreamCommon(new Stream(&stream->common)),
145       mStreamMmap(new StreamMmap<audio_stream_in_t>(stream)),
146       mEfGroup(nullptr),
147       mStopReadThread(false) {}
148 
~StreamIn()149 StreamIn::~StreamIn() {
150     ATRACE_CALL();
151     close();
152     if (mReadThread.get()) {
153         ATRACE_NAME("mReadThread->join");
154         status_t status = mReadThread->join();
155         ALOGE_IF(status, "read thread exit error: %s", strerror(-status));
156     }
157     if (mEfGroup) {
158         status_t status = EventFlag::deleteEventFlag(&mEfGroup);
159         ALOGE_IF(status, "read MQ event flag deletion error: %s", strerror(-status));
160     }
161 #if MAJOR_VERSION <= 5
162     mDevice->closeInputStream(mStream);
163 #endif
164     mStream = nullptr;
165 }
166 
167 // Methods from ::android::hardware::audio::CPP_VERSION::IStream follow.
getFrameSize()168 Return<uint64_t> StreamIn::getFrameSize() {
169     return audio_stream_in_frame_size(mStream);
170 }
171 
getFrameCount()172 Return<uint64_t> StreamIn::getFrameCount() {
173     return mStreamCommon->getFrameCount();
174 }
175 
getBufferSize()176 Return<uint64_t> StreamIn::getBufferSize() {
177     return mStreamCommon->getBufferSize();
178 }
179 
getSampleRate()180 Return<uint32_t> StreamIn::getSampleRate() {
181     return mStreamCommon->getSampleRate();
182 }
183 
184 #if MAJOR_VERSION == 2
getSupportedChannelMasks(getSupportedChannelMasks_cb _hidl_cb)185 Return<void> StreamIn::getSupportedChannelMasks(getSupportedChannelMasks_cb _hidl_cb) {
186     return mStreamCommon->getSupportedChannelMasks(_hidl_cb);
187 }
getSupportedSampleRates(getSupportedSampleRates_cb _hidl_cb)188 Return<void> StreamIn::getSupportedSampleRates(getSupportedSampleRates_cb _hidl_cb) {
189     return mStreamCommon->getSupportedSampleRates(_hidl_cb);
190 }
191 #endif
192 
getSupportedChannelMasks(AudioFormat format,getSupportedChannelMasks_cb _hidl_cb)193 Return<void> StreamIn::getSupportedChannelMasks(AudioFormat format,
194                                                 getSupportedChannelMasks_cb _hidl_cb) {
195     return mStreamCommon->getSupportedChannelMasks(format, _hidl_cb);
196 }
getSupportedSampleRates(AudioFormat format,getSupportedSampleRates_cb _hidl_cb)197 Return<void> StreamIn::getSupportedSampleRates(AudioFormat format,
198                                                getSupportedSampleRates_cb _hidl_cb) {
199     return mStreamCommon->getSupportedSampleRates(format, _hidl_cb);
200 }
201 
setSampleRate(uint32_t sampleRateHz)202 Return<Result> StreamIn::setSampleRate(uint32_t sampleRateHz) {
203     return mStreamCommon->setSampleRate(sampleRateHz);
204 }
205 
getChannelMask()206 Return<AudioChannelBitfield> StreamIn::getChannelMask() {
207     return mStreamCommon->getChannelMask();
208 }
209 
setChannelMask(AudioChannelBitfield mask)210 Return<Result> StreamIn::setChannelMask(AudioChannelBitfield mask) {
211     return mStreamCommon->setChannelMask(mask);
212 }
213 
getFormat()214 Return<AudioFormat> StreamIn::getFormat() {
215     return mStreamCommon->getFormat();
216 }
217 
getSupportedFormats(getSupportedFormats_cb _hidl_cb)218 Return<void> StreamIn::getSupportedFormats(getSupportedFormats_cb _hidl_cb) {
219     return mStreamCommon->getSupportedFormats(_hidl_cb);
220 }
221 
setFormat(AudioFormat format)222 Return<Result> StreamIn::setFormat(AudioFormat format) {
223     return mStreamCommon->setFormat(format);
224 }
225 
getAudioProperties(getAudioProperties_cb _hidl_cb)226 Return<void> StreamIn::getAudioProperties(getAudioProperties_cb _hidl_cb) {
227     return mStreamCommon->getAudioProperties(_hidl_cb);
228 }
229 
addEffect(uint64_t effectId)230 Return<Result> StreamIn::addEffect(uint64_t effectId) {
231     return mStreamCommon->addEffect(effectId);
232 }
233 
removeEffect(uint64_t effectId)234 Return<Result> StreamIn::removeEffect(uint64_t effectId) {
235     return mStreamCommon->removeEffect(effectId);
236 }
237 
standby()238 Return<Result> StreamIn::standby() {
239     return mStreamCommon->standby();
240 }
241 
setHwAvSync(uint32_t hwAvSync)242 Return<Result> StreamIn::setHwAvSync(uint32_t hwAvSync) {
243     return mStreamCommon->setHwAvSync(hwAvSync);
244 }
245 
246 #if MAJOR_VERSION == 2
setConnectedState(const DeviceAddress & address,bool connected)247 Return<Result> StreamIn::setConnectedState(const DeviceAddress& address, bool connected) {
248     return mStreamCommon->setConnectedState(address, connected);
249 }
250 
getDevice()251 Return<AudioDevice> StreamIn::getDevice() {
252     return mStreamCommon->getDevice();
253 }
254 
setDevice(const DeviceAddress & address)255 Return<Result> StreamIn::setDevice(const DeviceAddress& address) {
256     return mStreamCommon->setDevice(address);
257 }
258 
getParameters(const hidl_vec<hidl_string> & keys,getParameters_cb _hidl_cb)259 Return<void> StreamIn::getParameters(const hidl_vec<hidl_string>& keys, getParameters_cb _hidl_cb) {
260     return mStreamCommon->getParameters(keys, _hidl_cb);
261 }
262 
setParameters(const hidl_vec<ParameterValue> & parameters)263 Return<Result> StreamIn::setParameters(const hidl_vec<ParameterValue>& parameters) {
264     return mStreamCommon->setParameters(parameters);
265 }
266 
debugDump(const hidl_handle & fd)267 Return<void> StreamIn::debugDump(const hidl_handle& fd) {
268     return mStreamCommon->debugDump(fd);
269 }
270 #elif MAJOR_VERSION >= 4
getDevices(getDevices_cb _hidl_cb)271 Return<void> StreamIn::getDevices(getDevices_cb _hidl_cb) {
272     return mStreamCommon->getDevices(_hidl_cb);
273 }
274 
setDevices(const hidl_vec<DeviceAddress> & devices)275 Return<Result> StreamIn::setDevices(const hidl_vec<DeviceAddress>& devices) {
276     return mStreamCommon->setDevices(devices);
277 }
getParameters(const hidl_vec<ParameterValue> & context,const hidl_vec<hidl_string> & keys,getParameters_cb _hidl_cb)278 Return<void> StreamIn::getParameters(const hidl_vec<ParameterValue>& context,
279                                      const hidl_vec<hidl_string>& keys, getParameters_cb _hidl_cb) {
280     return mStreamCommon->getParameters(context, keys, _hidl_cb);
281 }
282 
setParameters(const hidl_vec<ParameterValue> & context,const hidl_vec<ParameterValue> & parameters)283 Return<Result> StreamIn::setParameters(const hidl_vec<ParameterValue>& context,
284                                        const hidl_vec<ParameterValue>& parameters) {
285     return mStreamCommon->setParameters(context, parameters);
286 }
287 #endif
288 
start()289 Return<Result> StreamIn::start() {
290     return mStreamMmap->start();
291 }
292 
stop()293 Return<Result> StreamIn::stop() {
294     return mStreamMmap->stop();
295 }
296 
createMmapBuffer(int32_t minSizeFrames,createMmapBuffer_cb _hidl_cb)297 Return<void> StreamIn::createMmapBuffer(int32_t minSizeFrames, createMmapBuffer_cb _hidl_cb) {
298     return mStreamMmap->createMmapBuffer(minSizeFrames, audio_stream_in_frame_size(mStream),
299                                          _hidl_cb);
300 }
301 
getMmapPosition(getMmapPosition_cb _hidl_cb)302 Return<void> StreamIn::getMmapPosition(getMmapPosition_cb _hidl_cb) {
303     return mStreamMmap->getMmapPosition(_hidl_cb);
304 }
305 
close()306 Return<Result> StreamIn::close() {
307     if (mStopReadThread.load(std::memory_order_relaxed)) {  // only this thread writes
308         return Result::INVALID_STATE;
309     }
310     mStopReadThread.store(true, std::memory_order_release);
311     if (mEfGroup) {
312         mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL));
313     }
314 #if MAJOR_VERSION >= 6
315     mDevice->closeInputStream(mStream);
316 #endif
317     return Result::OK;
318 }
319 
320 // Methods from ::android::hardware::audio::CPP_VERSION::IStreamIn follow.
getAudioSource(getAudioSource_cb _hidl_cb)321 Return<void> StreamIn::getAudioSource(getAudioSource_cb _hidl_cb) {
322     int halSource;
323     Result retval = mStreamCommon->getParam(AudioParameter::keyInputSource, &halSource);
324     AudioSource source(AudioSource::DEFAULT);
325     if (retval == Result::OK) {
326         source = AudioSource(halSource);
327     }
328     _hidl_cb(retval, source);
329     return Void();
330 }
331 
setGain(float gain)332 Return<Result> StreamIn::setGain(float gain) {
333     if (!isGainNormalized(gain)) {
334         ALOGW("Can not set a stream input gain (%f) outside [0,1]", gain);
335         return Result::INVALID_ARGUMENTS;
336     }
337     return Stream::analyzeStatus("set_gain", mStream->set_gain(mStream, gain));
338 }
339 
prepareForReading(uint32_t frameSize,uint32_t framesCount,prepareForReading_cb _hidl_cb)340 Return<void> StreamIn::prepareForReading(uint32_t frameSize, uint32_t framesCount,
341                                          prepareForReading_cb _hidl_cb) {
342     status_t status;
343     ThreadInfo threadInfo = {0, 0};
344 
345     // Wrap the _hidl_cb to return an error
346     auto sendError = [&threadInfo, &_hidl_cb](Result result) {
347         _hidl_cb(result, CommandMQ::Descriptor(), DataMQ::Descriptor(), StatusMQ::Descriptor(),
348                  threadInfo);
349     };
350 
351     // Create message queues.
352     if (mDataMQ) {
353         ALOGE("the client attempts to call prepareForReading twice");
354         sendError(Result::INVALID_STATE);
355         return Void();
356     }
357     std::unique_ptr<CommandMQ> tempCommandMQ(new CommandMQ(1));
358 
359     // Check frameSize and framesCount
360     if (frameSize == 0 || framesCount == 0) {
361         ALOGE("Null frameSize (%u) or framesCount (%u)", frameSize, framesCount);
362         sendError(Result::INVALID_ARGUMENTS);
363         return Void();
364     }
365 
366     if (frameSize > Stream::MAX_BUFFER_SIZE / framesCount) {
367         ALOGE("Buffer too big: %u*%u bytes > MAX_BUFFER_SIZE (%u)", frameSize, framesCount,
368               Stream::MAX_BUFFER_SIZE);
369         sendError(Result::INVALID_ARGUMENTS);
370         return Void();
371     }
372     std::unique_ptr<DataMQ> tempDataMQ(new DataMQ(frameSize * framesCount, true /* EventFlag */));
373 
374     std::unique_ptr<StatusMQ> tempStatusMQ(new StatusMQ(1));
375     if (!tempCommandMQ->isValid() || !tempDataMQ->isValid() || !tempStatusMQ->isValid()) {
376         ALOGE_IF(!tempCommandMQ->isValid(), "command MQ is invalid");
377         ALOGE_IF(!tempDataMQ->isValid(), "data MQ is invalid");
378         ALOGE_IF(!tempStatusMQ->isValid(), "status MQ is invalid");
379         sendError(Result::INVALID_ARGUMENTS);
380         return Void();
381     }
382     EventFlag* tempRawEfGroup{};
383     status = EventFlag::createEventFlag(tempDataMQ->getEventFlagWord(), &tempRawEfGroup);
384     std::unique_ptr<EventFlag, void (*)(EventFlag*)> tempElfGroup(
385         tempRawEfGroup, [](auto* ef) { EventFlag::deleteEventFlag(&ef); });
386     if (status != OK || !tempElfGroup) {
387         ALOGE("failed creating event flag for data MQ: %s", strerror(-status));
388         sendError(Result::INVALID_ARGUMENTS);
389         return Void();
390     }
391 
392     // Create and launch the thread.
393     auto tempReadThread =
394         std::make_unique<ReadThread>(&mStopReadThread, mStream, tempCommandMQ.get(),
395                                      tempDataMQ.get(), tempStatusMQ.get(), tempElfGroup.get());
396     if (!tempReadThread->init()) {
397         ALOGW("failed to start reader thread: %s", strerror(-status));
398         sendError(Result::INVALID_ARGUMENTS);
399         return Void();
400     }
401     status = tempReadThread->run("reader", PRIORITY_URGENT_AUDIO);
402     if (status != OK) {
403         ALOGW("failed to start reader thread: %s", strerror(-status));
404         sendError(Result::INVALID_ARGUMENTS);
405         return Void();
406     }
407 
408     mCommandMQ = std::move(tempCommandMQ);
409     mDataMQ = std::move(tempDataMQ);
410     mStatusMQ = std::move(tempStatusMQ);
411     mReadThread = tempReadThread.release();
412     mEfGroup = tempElfGroup.release();
413     threadInfo.pid = getpid();
414     threadInfo.tid = mReadThread->getTid();
415     _hidl_cb(Result::OK, *mCommandMQ->getDesc(), *mDataMQ->getDesc(), *mStatusMQ->getDesc(),
416              threadInfo);
417     return Void();
418 }
419 
getInputFramesLost()420 Return<uint32_t> StreamIn::getInputFramesLost() {
421     return mStream->get_input_frames_lost(mStream);
422 }
423 
424 // static
getCapturePositionImpl(audio_stream_in_t * stream,uint64_t * frames,uint64_t * time)425 Result StreamIn::getCapturePositionImpl(audio_stream_in_t* stream, uint64_t* frames,
426                                         uint64_t* time) {
427     // HAL may have a stub function, always returning ENOSYS, don't
428     // spam the log in this case.
429     static const std::vector<int> ignoredErrors{ENOSYS};
430     Result retval(Result::NOT_SUPPORTED);
431     if (stream->get_capture_position == NULL) return retval;
432     int64_t halFrames, halTime;
433     retval = Stream::analyzeStatus("get_capture_position",
434                                    stream->get_capture_position(stream, &halFrames, &halTime),
435                                    ignoredErrors);
436     if (retval == Result::OK) {
437         *frames = halFrames;
438         *time = halTime;
439     }
440     return retval;
441 };
442 
getCapturePosition(getCapturePosition_cb _hidl_cb)443 Return<void> StreamIn::getCapturePosition(getCapturePosition_cb _hidl_cb) {
444     uint64_t frames = 0, time = 0;
445     Result retval = getCapturePositionImpl(mStream, &frames, &time);
446     _hidl_cb(retval, frames, time);
447     return Void();
448 }
449 
debug(const hidl_handle & fd,const hidl_vec<hidl_string> & options)450 Return<void> StreamIn::debug(const hidl_handle& fd, const hidl_vec<hidl_string>& options) {
451     return mStreamCommon->debug(fd, options);
452 }
453 
454 #if MAJOR_VERSION >= 4
updateSinkMetadata(const SinkMetadata & sinkMetadata)455 Return<void> StreamIn::updateSinkMetadata(const SinkMetadata& sinkMetadata) {
456     if (mStream->update_sink_metadata == nullptr) {
457         return Void();  // not supported by the HAL
458     }
459     std::vector<record_track_metadata> halTracks;
460     halTracks.reserve(sinkMetadata.tracks.size());
461     for (auto& metadata : sinkMetadata.tracks) {
462         record_track_metadata halTrackMetadata = {
463             .source = static_cast<audio_source_t>(metadata.source), .gain = metadata.gain};
464 #if MAJOR_VERSION >= 5
465         if (metadata.destination.getDiscriminator() ==
466             RecordTrackMetadata::Destination::hidl_discriminator::device) {
467             halTrackMetadata.dest_device =
468                 static_cast<audio_devices_t>(metadata.destination.device().device);
469             strncpy(halTrackMetadata.dest_device_address,
470                     deviceAddressToHal(metadata.destination.device()).c_str(),
471                     AUDIO_DEVICE_MAX_ADDRESS_LEN);
472         }
473 #endif
474         halTracks.push_back(halTrackMetadata);
475     }
476     const sink_metadata_t halMetadata = {
477         .track_count = halTracks.size(),
478         .tracks = halTracks.data(),
479     };
480     mStream->update_sink_metadata(mStream, &halMetadata);
481     return Void();
482 }
483 
getActiveMicrophones(getActiveMicrophones_cb _hidl_cb)484 Return<void> StreamIn::getActiveMicrophones(getActiveMicrophones_cb _hidl_cb) {
485     Result retval = Result::NOT_SUPPORTED;
486     size_t actual_mics = AUDIO_MICROPHONE_MAX_COUNT;
487     audio_microphone_characteristic_t mic_array[AUDIO_MICROPHONE_MAX_COUNT];
488 
489     hidl_vec<MicrophoneInfo> microphones;
490     if (mStream->get_active_microphones != NULL &&
491         mStream->get_active_microphones(mStream, &mic_array[0], &actual_mics) == 0) {
492         microphones.resize(actual_mics);
493         for (size_t i = 0; i < actual_mics; ++i) {
494             halToMicrophoneCharacteristics(&microphones[i], mic_array[i]);
495         }
496         retval = Result::OK;
497     }
498 
499     _hidl_cb(retval, microphones);
500     return Void();
501 }
502 #endif
503 
504 #if MAJOR_VERSION >= 5
setMicrophoneDirection(MicrophoneDirection direction)505 Return<Result> StreamIn::setMicrophoneDirection(MicrophoneDirection direction) {
506     if (mStream->set_microphone_direction == nullptr) {
507         return Result::NOT_SUPPORTED;
508     }
509     if (!common::utils::isValidHidlEnum(direction)) {
510         ALOGE("%s: Invalid direction %d", __func__, direction);
511         return Result::INVALID_ARGUMENTS;
512     }
513     return Stream::analyzeStatus(
514             "set_microphone_direction",
515             mStream->set_microphone_direction(
516                     mStream, static_cast<audio_microphone_direction_t>(direction)));
517 }
518 
setMicrophoneFieldDimension(float zoom)519 Return<Result> StreamIn::setMicrophoneFieldDimension(float zoom) {
520     if (mStream->set_microphone_field_dimension == nullptr) {
521         return Result::NOT_SUPPORTED;
522     }
523     if (std::isnan(zoom) || zoom < -1 || zoom > 1) {
524         ALOGE("%s: Invalid zoom %f", __func__, zoom);
525         return Result::INVALID_ARGUMENTS;
526     }
527     return Stream::analyzeStatus("set_microphone_field_dimension",
528                                  mStream->set_microphone_field_dimension(mStream, zoom));
529 }
530 
531 #endif
532 
533 }  // namespace implementation
534 }  // namespace CPP_VERSION
535 }  // namespace audio
536 }  // namespace hardware
537 }  // namespace android
538