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(µphones[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