1 /*
2  * Copyright (C) 2016 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 "StreamHalLocal"
18 //#define LOG_NDEBUG 0
19 
20 #include <audio_utils/Metadata.h>
21 #include <hardware/audio.h>
22 #include <media/AudioParameter.h>
23 #include <utils/Log.h>
24 
25 #include "DeviceHalLocal.h"
26 #include "ParameterUtils.h"
27 #include "StreamHalLocal.h"
28 
29 namespace android {
30 namespace CPP_VERSION {
31 
StreamHalLocal(audio_stream_t * stream,sp<DeviceHalLocal> device)32 StreamHalLocal::StreamHalLocal(audio_stream_t *stream, sp<DeviceHalLocal> device)
33         : mDevice(device),
34           mStream(stream) {
35     // Instrument audio signal power logging.
36     // Note: This assumes channel mask, format, and sample rate do not change after creation.
37     if (mStream != nullptr /* && mStreamPowerLog.isUserDebugOrEngBuild() */) {
38         mStreamPowerLog.init(mStream->get_sample_rate(mStream),
39                 mStream->get_channels(mStream),
40                 mStream->get_format(mStream));
41     }
42 }
43 
~StreamHalLocal()44 StreamHalLocal::~StreamHalLocal() {
45     mStream = 0;
46     mDevice.clear();
47 }
48 
getBufferSize(size_t * size)49 status_t StreamHalLocal::getBufferSize(size_t *size) {
50     *size = mStream->get_buffer_size(mStream);
51     return OK;
52 }
53 
getAudioProperties(audio_config_base_t * configBase)54 status_t StreamHalLocal::getAudioProperties(audio_config_base_t *configBase) {
55     configBase->sample_rate = mStream->get_sample_rate(mStream);
56     configBase->channel_mask = mStream->get_channels(mStream);
57     configBase->format = mStream->get_format(mStream);
58     return OK;
59 }
60 
setParameters(const String8 & kvPairs)61 status_t StreamHalLocal::setParameters(const String8& kvPairs) {
62     return mStream->set_parameters(mStream, kvPairs.string());
63 }
64 
getParameters(const String8 & keys,String8 * values)65 status_t StreamHalLocal::getParameters(const String8& keys, String8 *values) {
66     char *halValues = mStream->get_parameters(mStream, keys.string());
67     if (halValues != NULL) {
68         values->setTo(halValues);
69         free(halValues);
70     } else {
71         values->clear();
72     }
73     return OK;
74 }
75 
addEffect(sp<EffectHalInterface>)76 status_t StreamHalLocal::addEffect(sp<EffectHalInterface>) {
77     LOG_ALWAYS_FATAL("Local streams can not have effects");
78     return INVALID_OPERATION;
79 }
80 
removeEffect(sp<EffectHalInterface>)81 status_t StreamHalLocal::removeEffect(sp<EffectHalInterface>) {
82     LOG_ALWAYS_FATAL("Local streams can not have effects");
83     return INVALID_OPERATION;
84 }
85 
standby()86 status_t StreamHalLocal::standby() {
87     return mStream->standby(mStream);
88 }
89 
dump(int fd,const Vector<String16> & args)90 status_t StreamHalLocal::dump(int fd, const Vector<String16>& args) {
91     (void) args;
92     status_t status = mStream->dump(mStream, fd);
93     mStreamPowerLog.dump(fd);
94     return status;
95 }
96 
setHalThreadPriority(int)97 status_t StreamHalLocal::setHalThreadPriority(int) {
98     // Don't need to do anything as local hal is executed by audioflinger directly
99     // on the same thread.
100     return OK;
101 }
102 
StreamOutHalLocal(audio_stream_out_t * stream,sp<DeviceHalLocal> device)103 StreamOutHalLocal::StreamOutHalLocal(audio_stream_out_t *stream, sp<DeviceHalLocal> device)
104         : StreamHalLocal(&stream->common, device), mStream(stream) {
105 }
106 
~StreamOutHalLocal()107 StreamOutHalLocal::~StreamOutHalLocal() {
108     mCallback.clear();
109     mDevice->closeOutputStream(mStream);
110     mStream = 0;
111 }
112 
getFrameSize(size_t * size)113 status_t StreamOutHalLocal::getFrameSize(size_t *size) {
114     *size = audio_stream_out_frame_size(mStream);
115     return OK;
116 }
117 
getLatency(uint32_t * latency)118 status_t StreamOutHalLocal::getLatency(uint32_t *latency) {
119     *latency = mStream->get_latency(mStream);
120     return OK;
121 }
122 
setVolume(float left,float right)123 status_t StreamOutHalLocal::setVolume(float left, float right) {
124     if (mStream->set_volume == NULL) return INVALID_OPERATION;
125     return mStream->set_volume(mStream, left, right);
126 }
127 
selectPresentation(int presentationId,int programId)128 status_t StreamOutHalLocal::selectPresentation(int presentationId, int programId) {
129     AudioParameter param;
130     param.addInt(String8(AudioParameter::keyPresentationId), presentationId);
131     param.addInt(String8(AudioParameter::keyProgramId), programId);
132     return setParameters(param.toString());
133 }
134 
write(const void * buffer,size_t bytes,size_t * written)135 status_t StreamOutHalLocal::write(const void *buffer, size_t bytes, size_t *written) {
136     ssize_t writeResult = mStream->write(mStream, buffer, bytes);
137     if (writeResult > 0) {
138         *written = writeResult;
139         mStreamPowerLog.log(buffer, *written);
140         return OK;
141     } else {
142         *written = 0;
143         return writeResult;
144     }
145 }
146 
getRenderPosition(uint32_t * dspFrames)147 status_t StreamOutHalLocal::getRenderPosition(uint32_t *dspFrames) {
148     return mStream->get_render_position(mStream, dspFrames);
149 }
150 
getNextWriteTimestamp(int64_t * timestamp)151 status_t StreamOutHalLocal::getNextWriteTimestamp(int64_t *timestamp) {
152     if (mStream->get_next_write_timestamp == NULL) return INVALID_OPERATION;
153     return mStream->get_next_write_timestamp(mStream, timestamp);
154 }
155 
setCallback(wp<StreamOutHalInterfaceCallback> callback)156 status_t StreamOutHalLocal::setCallback(wp<StreamOutHalInterfaceCallback> callback) {
157     if (mStream->set_callback == NULL) return INVALID_OPERATION;
158     status_t result = mStream->set_callback(mStream, StreamOutHalLocal::asyncCallback, this);
159     if (result == OK) {
160         mCallback = callback;
161     }
162     return result;
163 }
164 
165 // static
asyncCallback(stream_callback_event_t event,void *,void * cookie)166 int StreamOutHalLocal::asyncCallback(stream_callback_event_t event, void*, void *cookie) {
167     // We act as if we gave a wp<StreamOutHalLocal> to HAL. This way we should handle
168     // correctly the case when the callback is invoked while StreamOutHalLocal's destructor is
169     // already running, because the destructor is invoked after the refcount has been atomically
170     // decremented.
171     wp<StreamOutHalLocal> weakSelf(static_cast<StreamOutHalLocal*>(cookie));
172     sp<StreamOutHalLocal> self = weakSelf.promote();
173     if (self == 0) return 0;
174     sp<StreamOutHalInterfaceCallback> callback = self->mCallback.promote();
175     if (callback == 0) return 0;
176     ALOGV("asyncCallback() event %d", event);
177     switch (event) {
178         case STREAM_CBK_EVENT_WRITE_READY:
179             callback->onWriteReady();
180             break;
181         case STREAM_CBK_EVENT_DRAIN_READY:
182             callback->onDrainReady();
183             break;
184         case STREAM_CBK_EVENT_ERROR:
185             callback->onError();
186             break;
187         default:
188             ALOGW("asyncCallback() unknown event %d", event);
189             break;
190     }
191     return 0;
192 }
193 
supportsPauseAndResume(bool * supportsPause,bool * supportsResume)194 status_t StreamOutHalLocal::supportsPauseAndResume(bool *supportsPause, bool *supportsResume) {
195     *supportsPause = mStream->pause != NULL;
196     *supportsResume = mStream->resume != NULL;
197     return OK;
198 }
199 
pause()200 status_t StreamOutHalLocal::pause() {
201     if (mStream->pause == NULL) return INVALID_OPERATION;
202     return mStream->pause(mStream);
203 }
204 
resume()205 status_t StreamOutHalLocal::resume() {
206     if (mStream->resume == NULL) return INVALID_OPERATION;
207     return mStream->resume(mStream);
208 }
209 
supportsDrain(bool * supportsDrain)210 status_t StreamOutHalLocal::supportsDrain(bool *supportsDrain) {
211     *supportsDrain = mStream->drain != NULL;
212     return OK;
213 }
214 
drain(bool earlyNotify)215 status_t StreamOutHalLocal::drain(bool earlyNotify) {
216     if (mStream->drain == NULL) return INVALID_OPERATION;
217     return mStream->drain(mStream, earlyNotify ? AUDIO_DRAIN_EARLY_NOTIFY : AUDIO_DRAIN_ALL);
218 }
219 
flush()220 status_t StreamOutHalLocal::flush() {
221     if (mStream->flush == NULL) return INVALID_OPERATION;
222     return mStream->flush(mStream);
223 }
224 
getPresentationPosition(uint64_t * frames,struct timespec * timestamp)225 status_t StreamOutHalLocal::getPresentationPosition(uint64_t *frames, struct timespec *timestamp) {
226     if (mStream->get_presentation_position == NULL) return INVALID_OPERATION;
227     return mStream->get_presentation_position(mStream, frames, timestamp);
228 }
229 
doUpdateSourceMetadata(const SourceMetadata & sourceMetadata)230 void StreamOutHalLocal::doUpdateSourceMetadata(const SourceMetadata& sourceMetadata) {
231     std::vector<playback_track_metadata> halTracks;
232     halTracks.reserve(sourceMetadata.tracks.size());
233     for (auto& metadata : sourceMetadata.tracks) {
234         playback_track_metadata halTrackMetadata;
235         playback_track_metadata_from_v7(&halTrackMetadata, &metadata);
236         halTracks.push_back(halTrackMetadata);
237     }
238     const source_metadata_t halMetadata = {
239         .track_count = halTracks.size(),
240         .tracks = halTracks.data(),
241     };
242     mStream->update_source_metadata(mStream, &halMetadata);
243 }
244 
245 #if MAJOR_VERSION >= 7
doUpdateSourceMetadataV7(const SourceMetadata & sourceMetadata)246 void StreamOutHalLocal::doUpdateSourceMetadataV7(const SourceMetadata& sourceMetadata) {
247     const source_metadata_v7_t metadata {
248         .track_count = sourceMetadata.tracks.size(),
249         // const cast is fine as it is in a const structure
250         .tracks = const_cast<playback_track_metadata_v7*>(sourceMetadata.tracks.data()),
251     };
252     mStream->update_source_metadata_v7(mStream, &metadata);
253 }
254 #endif
255 
updateSourceMetadata(const SourceMetadata & sourceMetadata)256 status_t StreamOutHalLocal::updateSourceMetadata(const SourceMetadata& sourceMetadata) {
257 #if MAJOR_VERSION < 7
258     if (mStream->update_source_metadata == nullptr) {
259         return INVALID_OPERATION;
260     }
261     doUpdateSourceMetadata(sourceMetadata);
262 #else
263     if (mDevice->version() < AUDIO_DEVICE_API_VERSION_3_2) {
264         if (mStream->update_source_metadata == nullptr) {
265             return INVALID_OPERATION;
266         }
267         doUpdateSourceMetadata(sourceMetadata);
268     } else {
269         if (mStream->update_source_metadata_v7 == nullptr) {
270             return INVALID_OPERATION;
271         }
272         doUpdateSourceMetadataV7(sourceMetadata);
273     }
274 #endif
275     return OK;
276 }
277 
278 
start()279 status_t StreamOutHalLocal::start() {
280     if (mStream->start == NULL) return INVALID_OPERATION;
281     return mStream->start(mStream);
282 }
283 
stop()284 status_t StreamOutHalLocal::stop() {
285     if (mStream->stop == NULL) return INVALID_OPERATION;
286     return mStream->stop(mStream);
287 }
288 
createMmapBuffer(int32_t minSizeFrames,struct audio_mmap_buffer_info * info)289 status_t StreamOutHalLocal::createMmapBuffer(int32_t minSizeFrames,
290                                   struct audio_mmap_buffer_info *info) {
291     if (mStream->create_mmap_buffer == NULL) return INVALID_OPERATION;
292     return mStream->create_mmap_buffer(mStream, minSizeFrames, info);
293 }
294 
getMmapPosition(struct audio_mmap_position * position)295 status_t StreamOutHalLocal::getMmapPosition(struct audio_mmap_position *position) {
296     if (mStream->get_mmap_position == NULL) return INVALID_OPERATION;
297     return mStream->get_mmap_position(mStream, position);
298 }
299 
getDualMonoMode(audio_dual_mono_mode_t * mode)300 status_t StreamOutHalLocal::getDualMonoMode(audio_dual_mono_mode_t* mode) {
301     if (mStream->get_dual_mono_mode == nullptr) return INVALID_OPERATION;
302     return mStream->get_dual_mono_mode(mStream, mode);
303 }
304 
setDualMonoMode(audio_dual_mono_mode_t mode)305 status_t StreamOutHalLocal::setDualMonoMode(audio_dual_mono_mode_t mode) {
306     if (mStream->set_dual_mono_mode == nullptr) return INVALID_OPERATION;
307     return mStream->set_dual_mono_mode(mStream, mode);
308 }
309 
getAudioDescriptionMixLevel(float * leveldB)310 status_t StreamOutHalLocal::getAudioDescriptionMixLevel(float* leveldB) {
311     if (mStream->get_audio_description_mix_level == nullptr) return INVALID_OPERATION;
312     return mStream->get_audio_description_mix_level(mStream, leveldB);
313 }
314 
setAudioDescriptionMixLevel(float leveldB)315 status_t StreamOutHalLocal::setAudioDescriptionMixLevel(float leveldB) {
316     if (mStream->set_audio_description_mix_level == nullptr) return INVALID_OPERATION;
317     return mStream->set_audio_description_mix_level(mStream, leveldB);
318 }
319 
getPlaybackRateParameters(audio_playback_rate_t * playbackRate)320 status_t StreamOutHalLocal::getPlaybackRateParameters(audio_playback_rate_t* playbackRate) {
321     if (mStream->get_playback_rate_parameters == nullptr) return INVALID_OPERATION;
322     return mStream->get_playback_rate_parameters(mStream, playbackRate);
323 }
324 
setPlaybackRateParameters(const audio_playback_rate_t & playbackRate)325 status_t StreamOutHalLocal::setPlaybackRateParameters(const audio_playback_rate_t& playbackRate) {
326     if (mStream->set_playback_rate_parameters == nullptr) return INVALID_OPERATION;
327     return mStream->set_playback_rate_parameters(mStream, &playbackRate);
328 }
329 
setEventCallback(const sp<StreamOutHalInterfaceEventCallback> & callback)330 status_t StreamOutHalLocal::setEventCallback(
331         const sp<StreamOutHalInterfaceEventCallback>& callback) {
332     if (mStream->set_event_callback == nullptr) {
333         return INVALID_OPERATION;
334     }
335     stream_event_callback_t asyncCallback =
336             callback == nullptr ? nullptr : StreamOutHalLocal::asyncEventCallback;
337     status_t result = mStream->set_event_callback(mStream, asyncCallback, this);
338     if (result == OK) {
339         mEventCallback = callback;
340     }
341     return result;
342 }
343 
344 // static
asyncEventCallback(stream_event_callback_type_t event,void * param,void * cookie)345 int StreamOutHalLocal::asyncEventCallback(
346         stream_event_callback_type_t event, void *param, void *cookie) {
347     // We act as if we gave a wp<StreamOutHalLocal> to HAL. This way we should handle
348     // correctly the case when the callback is invoked while StreamOutHalLocal's destructor is
349     // already running, because the destructor is invoked after the refcount has been atomically
350     // decremented.
351     wp<StreamOutHalLocal> weakSelf(static_cast<StreamOutHalLocal*>(cookie));
352     sp<StreamOutHalLocal> self = weakSelf.promote();
353     if (self == nullptr) return 0;
354     sp<StreamOutHalInterfaceEventCallback> callback = self->mEventCallback.promote();
355     if (callback.get() == nullptr) return 0;
356     switch (event) {
357         case STREAM_EVENT_CBK_TYPE_CODEC_FORMAT_CHANGED:
358             // void* param is the byte string buffer from byte_string_from_audio_metadata().
359             // As the byte string buffer may have embedded zeroes, we cannot use strlen()
360             callback->onCodecFormatChanged(std::basic_string<uint8_t>(
361                     (const uint8_t*)param,
362                     audio_utils::metadata::dataByteStringLen((const uint8_t*)param)));
363             break;
364         default:
365             ALOGW("%s unknown event %d", __func__, event);
366             break;
367     }
368     return 0;
369 }
370 
StreamInHalLocal(audio_stream_in_t * stream,sp<DeviceHalLocal> device)371 StreamInHalLocal::StreamInHalLocal(audio_stream_in_t *stream, sp<DeviceHalLocal> device)
372         : StreamHalLocal(&stream->common, device), mStream(stream) {
373 }
374 
~StreamInHalLocal()375 StreamInHalLocal::~StreamInHalLocal() {
376     mDevice->closeInputStream(mStream);
377     mStream = 0;
378 }
379 
getFrameSize(size_t * size)380 status_t StreamInHalLocal::getFrameSize(size_t *size) {
381     *size = audio_stream_in_frame_size(mStream);
382     return OK;
383 }
384 
setGain(float gain)385 status_t StreamInHalLocal::setGain(float gain) {
386     return mStream->set_gain(mStream, gain);
387 }
388 
read(void * buffer,size_t bytes,size_t * read)389 status_t StreamInHalLocal::read(void *buffer, size_t bytes, size_t *read) {
390     ssize_t readResult = mStream->read(mStream, buffer, bytes);
391     if (readResult > 0) {
392         *read = readResult;
393         mStreamPowerLog.log( buffer, *read);
394         return OK;
395     } else {
396         *read = 0;
397         return readResult;
398     }
399 }
400 
getInputFramesLost(uint32_t * framesLost)401 status_t StreamInHalLocal::getInputFramesLost(uint32_t *framesLost) {
402     *framesLost = mStream->get_input_frames_lost(mStream);
403     return OK;
404 }
405 
getCapturePosition(int64_t * frames,int64_t * time)406 status_t StreamInHalLocal::getCapturePosition(int64_t *frames, int64_t *time) {
407     if (mStream->get_capture_position == NULL) return INVALID_OPERATION;
408     return mStream->get_capture_position(mStream, frames, time);
409 }
410 
doUpdateSinkMetadata(const SinkMetadata & sinkMetadata)411 void StreamInHalLocal::doUpdateSinkMetadata(const SinkMetadata& sinkMetadata) {
412     std::vector<record_track_metadata> halTracks;
413     halTracks.reserve(sinkMetadata.tracks.size());
414     for (auto& metadata : sinkMetadata.tracks) {
415         record_track_metadata halTrackMetadata;
416         record_track_metadata_from_v7(&halTrackMetadata, &metadata);
417         halTracks.push_back(halTrackMetadata);
418     }
419     const sink_metadata_t halMetadata = {
420         .track_count = halTracks.size(),
421         .tracks = halTracks.data(),
422     };
423     mStream->update_sink_metadata(mStream, &halMetadata);
424 }
425 
426 #if MAJOR_VERSION >= 7
doUpdateSinkMetadataV7(const SinkMetadata & sinkMetadata)427 void StreamInHalLocal::doUpdateSinkMetadataV7(const SinkMetadata& sinkMetadata) {
428     const sink_metadata_v7_t halMetadata {
429         .track_count = sinkMetadata.tracks.size(),
430         // const cast is fine as it is in a const structure
431         .tracks = const_cast<record_track_metadata_v7*>(sinkMetadata.tracks.data()),
432     };
433     mStream->update_sink_metadata_v7(mStream, &halMetadata);
434 }
435 #endif
436 
updateSinkMetadata(const SinkMetadata & sinkMetadata)437 status_t StreamInHalLocal::updateSinkMetadata(const SinkMetadata& sinkMetadata) {
438 #if MAJOR_VERSION < 7
439     if (mStream->update_sink_metadata == nullptr) {
440         return INVALID_OPERATION;  // not supported by the HAL
441     }
442     doUpdateSinkMetadata(sinkMetadata);
443 #else
444     if (mDevice->version() < AUDIO_DEVICE_API_VERSION_3_2) {
445         if (mStream->update_sink_metadata == nullptr) {
446             return INVALID_OPERATION;  // not supported by the HAL
447         }
448         doUpdateSinkMetadata(sinkMetadata);
449     } else {
450         if (mStream->update_sink_metadata_v7 == nullptr) {
451             return INVALID_OPERATION;  // not supported by the HAL
452         }
453         doUpdateSinkMetadataV7(sinkMetadata);
454     }
455 #endif
456     return OK;
457 }
458 
start()459 status_t StreamInHalLocal::start() {
460     if (mStream->start == NULL) return INVALID_OPERATION;
461     return mStream->start(mStream);
462 }
463 
stop()464 status_t StreamInHalLocal::stop() {
465     if (mStream->stop == NULL) return INVALID_OPERATION;
466     return mStream->stop(mStream);
467 }
468 
createMmapBuffer(int32_t minSizeFrames,struct audio_mmap_buffer_info * info)469 status_t StreamInHalLocal::createMmapBuffer(int32_t minSizeFrames,
470                                   struct audio_mmap_buffer_info *info) {
471     if (mStream->create_mmap_buffer == NULL) return INVALID_OPERATION;
472     return mStream->create_mmap_buffer(mStream, minSizeFrames, info);
473 }
474 
getMmapPosition(struct audio_mmap_position * position)475 status_t StreamInHalLocal::getMmapPosition(struct audio_mmap_position *position) {
476     if (mStream->get_mmap_position == NULL) return INVALID_OPERATION;
477     return mStream->get_mmap_position(mStream, position);
478 }
479 
480 #if MAJOR_VERSION == 2
getActiveMicrophones(std::vector<media::MicrophoneInfo> * microphones __unused)481 status_t StreamInHalLocal::getActiveMicrophones(
482         std::vector<media::MicrophoneInfo> *microphones __unused) {
483     return INVALID_OPERATION;
484 }
485 #elif MAJOR_VERSION >= 4
getActiveMicrophones(std::vector<media::MicrophoneInfo> * microphones)486 status_t StreamInHalLocal::getActiveMicrophones(std::vector<media::MicrophoneInfo> *microphones) {
487     if (mStream->get_active_microphones == NULL) return INVALID_OPERATION;
488     size_t actual_mics = AUDIO_MICROPHONE_MAX_COUNT;
489     audio_microphone_characteristic_t mic_array[AUDIO_MICROPHONE_MAX_COUNT];
490     status_t status = mStream->get_active_microphones(mStream, &mic_array[0], &actual_mics);
491     for (size_t i = 0; i < actual_mics; i++) {
492         media::MicrophoneInfo microphoneInfo = media::MicrophoneInfo(mic_array[i]);
493         microphones->push_back(microphoneInfo);
494     }
495     return status;
496 }
497 #endif
498 
499 #if MAJOR_VERSION < 5
setPreferredMicrophoneDirection(audio_microphone_direction_t direction __unused)500 status_t StreamInHalLocal::setPreferredMicrophoneDirection(
501             audio_microphone_direction_t direction __unused) {
502     return INVALID_OPERATION;
503 }
504 
setPreferredMicrophoneFieldDimension(float zoom __unused)505 status_t StreamInHalLocal::setPreferredMicrophoneFieldDimension(float zoom __unused) {
506     return INVALID_OPERATION;
507 }
508 #else
setPreferredMicrophoneDirection(audio_microphone_direction_t direction)509 status_t StreamInHalLocal::setPreferredMicrophoneDirection(audio_microphone_direction_t direction) {
510     if (mStream->set_microphone_direction == NULL) return INVALID_OPERATION;
511     return mStream->set_microphone_direction(mStream, direction);
512 }
513 
setPreferredMicrophoneFieldDimension(float zoom)514 status_t StreamInHalLocal::setPreferredMicrophoneFieldDimension(float zoom) {
515     if (mStream->set_microphone_field_dimension == NULL) return INVALID_OPERATION;
516     return mStream->set_microphone_field_dimension(mStream, zoom);
517 
518 }
519 #endif
520 
521 } // namespace CPP_VERSION
522 } // namespace android
523 
524 
525