• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 #include <log/log.h>
18 #include <fmq/EventFlag.h>
19 #include <fmq/MessageQueue.h>
20 #include <hidl/MQDescriptor.h>
21 #include <hidl/Status.h>
22 #include <utils/ThreadDefs.h>
23 #include PATH(APM_XSD_ENUMS_H_FILENAME)
24 #include <future>
25 #include <thread>
26 #include "stream_out.h"
27 #include "device_port_sink.h"
28 #include "deleters.h"
29 #include "audio_ops.h"
30 #include "util.h"
31 #include "debug.h"
32 
33 namespace xsd {
34 using namespace ::android::audio::policy::configuration::CPP_VERSION;
35 }
36 
37 namespace android {
38 namespace hardware {
39 namespace audio {
40 namespace CPP_VERSION {
41 namespace implementation {
42 
43 using ::android::hardware::Void;
44 using namespace ::android::hardware::audio::common::COMMON_TYPES_CPP_VERSION;
45 using namespace ::android::hardware::audio::CORE_TYPES_CPP_VERSION;
46 
47 namespace {
48 
49 struct WriteThread : public IOThread {
50     typedef MessageQueue<IStreamOut::WriteCommand, kSynchronizedReadWrite> CommandMQ;
51     typedef MessageQueue<IStreamOut::WriteStatus, kSynchronizedReadWrite> StatusMQ;
52     typedef MessageQueue<uint8_t, kSynchronizedReadWrite> DataMQ;
53 
WriteThreadandroid::hardware::audio::CPP_VERSION::implementation::__anon06eeb58a0111::WriteThread54     WriteThread(StreamOut *stream, const size_t mqBufferSize)
55             : mStream(stream)
56             , mCommandMQ(1)
57             , mStatusMQ(1)
58             , mDataMQ(mqBufferSize, true /* EventFlag */) {
59         if (!mCommandMQ.isValid()) {
60             ALOGE("WriteThread::%s:%d: mCommandMQ is invalid", __func__, __LINE__);
61             return;
62         }
63         if (!mDataMQ.isValid()) {
64             ALOGE("WriteThread::%s:%d: mDataMQ is invalid", __func__, __LINE__);
65             return;
66         }
67         if (!mStatusMQ.isValid()) {
68             ALOGE("WriteThread::%s:%d: mStatusMQ is invalid", __func__, __LINE__);
69             return;
70         }
71 
72         status_t status;
73 
74         EventFlag* rawEfGroup = nullptr;
75         status = EventFlag::createEventFlag(mDataMQ.getEventFlagWord(), &rawEfGroup);
76         if (status != OK || !rawEfGroup) {
77             ALOGE("WriteThread::%s:%d: rawEfGroup is invalid", __func__, __LINE__);
78             return;
79         } else {
80             mEfGroup.reset(rawEfGroup);
81         }
82 
83         mThread = std::thread(&WriteThread::threadLoop, this);
84     }
85 
~WriteThreadandroid::hardware::audio::CPP_VERSION::implementation::__anon06eeb58a0111::WriteThread86     ~WriteThread() {
87         if (mThread.joinable()) {
88             requestExit();
89             mThread.join();
90         }
91     }
92 
getEventFlagandroid::hardware::audio::CPP_VERSION::implementation::__anon06eeb58a0111::WriteThread93     EventFlag *getEventFlag() override {
94         return mEfGroup.get();
95     }
96 
isRunningandroid::hardware::audio::CPP_VERSION::implementation::__anon06eeb58a0111::WriteThread97     bool isRunning() const {
98         return mThread.joinable();
99     }
100 
getTidandroid::hardware::audio::CPP_VERSION::implementation::__anon06eeb58a0111::WriteThread101     std::future<pthread_t> getTid() {
102         return mTid.get_future();
103     }
104 
105 
threadLoopandroid::hardware::audio::CPP_VERSION::implementation::__anon06eeb58a0111::WriteThread106     void threadLoop() {
107         util::setThreadPriority(PRIORITY_URGENT_AUDIO);
108         mTid.set_value(pthread_self());
109 
110         while (true) {
111             uint32_t efState = 0;
112             mEfGroup->wait(MessageQueueFlagBits::NOT_EMPTY | STAND_BY_REQUEST | EXIT_REQUEST,
113                            &efState);
114             if (efState & EXIT_REQUEST) {
115                 return;
116             }
117 
118             if (efState & STAND_BY_REQUEST) {
119                 mSink.reset();
120             }
121 
122             if (efState & (MessageQueueFlagBits::NOT_EMPTY | 0)) {
123                 if (!mSink) {
124                     mSink = DevicePortSink::create(mDataMQ.getQuantumCount(),
125                                                    mStream->getDeviceAddress(),
126                                                    mStream->getAudioConfig(),
127                                                    mStream->getAudioOutputFlags(),
128                                                    mStream->getFrameCounter());
129                     LOG_ALWAYS_FATAL_IF(!mSink);
130                 }
131 
132                 processCommand();
133             }
134         }
135     }
136 
processCommandandroid::hardware::audio::CPP_VERSION::implementation::__anon06eeb58a0111::WriteThread137     void processCommand() {
138         IStreamOut::WriteCommand wCommand;
139 
140         if (!mCommandMQ.read(&wCommand)) {
141             return;  // Nothing to do.
142         }
143 
144         IStreamOut::WriteStatus wStatus;
145         switch (wCommand) {
146             case IStreamOut::WriteCommand::WRITE:
147                 wStatus = doWrite();
148                 break;
149 
150             case IStreamOut::WriteCommand::GET_PRESENTATION_POSITION:
151                 wStatus = doGetPresentationPosition();
152                 break;
153 
154             case IStreamOut::WriteCommand::GET_LATENCY:
155                 wStatus = doGetLatency();
156                 break;
157 
158             default:
159                 ALOGE("WriteThread::%s:%d: Unknown write thread command code %d",
160                       __func__, __LINE__, wCommand);
161                 wStatus.retval = FAILURE(Result::NOT_SUPPORTED);
162                 break;
163         }
164 
165         wStatus.replyTo = wCommand;
166 
167         if (!mStatusMQ.write(&wStatus)) {
168             ALOGE("status message queue write failed");
169         }
170 
171         mEfGroup->wake(MessageQueueFlagBits::NOT_FULL | 0);
172     }
173 
doWriteandroid::hardware::audio::CPP_VERSION::implementation::__anon06eeb58a0111::WriteThread174     IStreamOut::WriteStatus doWrite() {
175         struct MQReader : public IReader {
176             explicit MQReader(DataMQ &mq) : dataMQ(mq) {}
177 
178             size_t operator()(void *dst, size_t sz) override {
179                 if (dataMQ.read(static_cast<uint8_t *>(dst), sz)) {
180                     totalRead += sz;
181                     return sz;
182                 } else {
183                     ALOGE("WriteThread::%s:%d: DataMQ::read failed",
184                           __func__, __LINE__);
185                     return 0;
186                 }
187             }
188 
189             size_t totalRead = 0;
190             DataMQ &dataMQ;
191         };
192 
193         MQReader reader(mDataMQ);
194         mSink->write(mStream->getEffectiveVolume(), mDataMQ.availableToRead(), reader);
195 
196         IStreamOut::WriteStatus status;
197         status.retval = Result::OK;
198         status.reply.written = reader.totalRead;
199         return status;
200     }
201 
doGetPresentationPositionandroid::hardware::audio::CPP_VERSION::implementation::__anon06eeb58a0111::WriteThread202     IStreamOut::WriteStatus doGetPresentationPosition() {
203         IStreamOut::WriteStatus status;
204 
205         status.retval = mSink->getPresentationPosition(
206             status.reply.presentationPosition.frames,
207             status.reply.presentationPosition.timeStamp);
208 
209         return status;
210     }
211 
doGetLatencyandroid::hardware::audio::CPP_VERSION::implementation::__anon06eeb58a0111::WriteThread212     IStreamOut::WriteStatus doGetLatency() {
213         IStreamOut::WriteStatus status;
214 
215         const int latencyMs =
216             DevicePortSink::getLatencyMs(mStream->getDeviceAddress(),
217                                          mStream->getAudioConfig());
218 
219         if (latencyMs >= 0) {
220             status.retval = Result::OK;
221             status.reply.latencyMs = latencyMs;
222         } else {
223             status.retval = Result::INVALID_STATE;
224         }
225 
226         return status;
227     }
228 
229     StreamOut *const mStream;
230     CommandMQ mCommandMQ;
231     StatusMQ mStatusMQ;
232     DataMQ mDataMQ;
233     std::unique_ptr<EventFlag, deleters::forEventFlag> mEfGroup;
234     std::unique_ptr<DevicePortSink> mSink;
235     std::thread mThread;
236     std::promise<pthread_t> mTid;
237 };
238 
239 } // namespace
240 
StreamOut(sp<Device> dev,int32_t ioHandle,const DeviceAddress & device,const AudioConfig & config,hidl_vec<AudioInOutFlag> flags,const SourceMetadata & sourceMetadata)241 StreamOut::StreamOut(sp<Device> dev,
242                      int32_t ioHandle,
243                      const DeviceAddress& device,
244                      const AudioConfig& config,
245                      hidl_vec<AudioInOutFlag> flags,
246                      const SourceMetadata& sourceMetadata)
247         : mDev(std::move(dev))
248         , mCommon(ioHandle, device, config, std::move(flags))
249         , mSourceMetadata(sourceMetadata) {}
250 
~StreamOut()251 StreamOut::~StreamOut() {
252     closeImpl(true);
253 }
254 
getFrameSize()255 Return<uint64_t> StreamOut::getFrameSize() {
256     return mCommon.getFrameSize();
257 }
258 
getFrameCount()259 Return<uint64_t> StreamOut::getFrameCount() {
260     return mCommon.getFrameCount();
261 }
262 
getBufferSize()263 Return<uint64_t> StreamOut::getBufferSize() {
264     return mCommon.getBufferSize();
265 }
266 
getSupportedProfiles(getSupportedProfiles_cb _hidl_cb)267 Return<void> StreamOut::getSupportedProfiles(getSupportedProfiles_cb _hidl_cb) {
268     mCommon.getSupportedProfiles(_hidl_cb);
269     return Void();
270 }
271 
getAudioProperties(getAudioProperties_cb _hidl_cb)272 Return<void> StreamOut::getAudioProperties(getAudioProperties_cb _hidl_cb) {
273     mCommon.getAudioProperties(_hidl_cb);
274     return Void();
275 }
276 
setAudioProperties(const AudioConfigBaseOptional & config)277 Return<Result> StreamOut::setAudioProperties(const AudioConfigBaseOptional& config) {
278     (void)config;
279     return FAILURE(Result::NOT_SUPPORTED);
280 }
281 
addEffect(uint64_t effectId)282 Return<Result> StreamOut::addEffect(uint64_t effectId) {
283     (void)effectId;
284     return FAILURE(Result::INVALID_ARGUMENTS);
285 }
286 
removeEffect(uint64_t effectId)287 Return<Result> StreamOut::removeEffect(uint64_t effectId) {
288     (void)effectId;
289     return FAILURE(Result::INVALID_ARGUMENTS);
290 }
291 
standby()292 Return<Result> StreamOut::standby() {
293     if (mWriteThread) {
294         LOG_ALWAYS_FATAL_IF(!mWriteThread->standby());
295     }
296 
297     return Result::OK;
298 }
299 
getDevices(getDevices_cb _hidl_cb)300 Return<void> StreamOut::getDevices(getDevices_cb _hidl_cb) {
301     mCommon.getDevices(_hidl_cb);
302     return Void();
303 }
304 
setDevices(const hidl_vec<DeviceAddress> & devices)305 Return<Result> StreamOut::setDevices(const hidl_vec<DeviceAddress>& devices) {
306     return mCommon.setDevices(devices);
307 }
308 
getParameters(const hidl_vec<ParameterValue> & context,const hidl_vec<hidl_string> & keys,getParameters_cb _hidl_cb)309 Return<void> StreamOut::getParameters(const hidl_vec<ParameterValue>& context,
310                                       const hidl_vec<hidl_string>& keys,
311                                       getParameters_cb _hidl_cb) {
312     (void)context;
313     _hidl_cb((keys.size() > 0) ? Result::NOT_SUPPORTED : Result::OK, {});
314     return Void();
315 }
316 
setParameters(const hidl_vec<ParameterValue> & context,const hidl_vec<ParameterValue> & parameters)317 Return<Result> StreamOut::setParameters(const hidl_vec<ParameterValue>& context,
318                                         const hidl_vec<ParameterValue>& parameters) {
319     (void)context;
320     (void)parameters;
321     return Result::OK;
322 }
323 
setHwAvSync(uint32_t hwAvSync)324 Return<Result> StreamOut::setHwAvSync(uint32_t hwAvSync) {
325     (void)hwAvSync;
326     return FAILURE(Result::NOT_SUPPORTED);
327 }
328 
closeImpl(const bool fromDctor)329 Result StreamOut::closeImpl(const bool fromDctor) {
330     if (mDev) {
331         mWriteThread.reset();
332         mDev->unrefDevice(this);
333         mDev = nullptr;
334         return Result::OK;
335     } else if (fromDctor) {
336         // closeImpl is always called from the dctor, it is ok if mDev is null,
337         // we don't want to log the error in this case.
338         return Result::OK;
339     } else {
340         return FAILURE(Result::INVALID_STATE);
341     }
342 }
343 
close()344 Return<Result> StreamOut::close() {
345     return closeImpl(false);
346 }
347 
start()348 Return<Result> StreamOut::start() {
349     return FAILURE(Result::NOT_SUPPORTED);
350 }
351 
stop()352 Return<Result> StreamOut::stop() {
353     return FAILURE(Result::NOT_SUPPORTED);
354 }
355 
createMmapBuffer(int32_t minSizeFrames,createMmapBuffer_cb _hidl_cb)356 Return<void> StreamOut::createMmapBuffer(int32_t minSizeFrames,
357                                          createMmapBuffer_cb _hidl_cb) {
358     (void)minSizeFrames;
359     _hidl_cb(FAILURE(Result::NOT_SUPPORTED), {});
360     return Void();
361 }
362 
getMmapPosition(getMmapPosition_cb _hidl_cb)363 Return<void> StreamOut::getMmapPosition(getMmapPosition_cb _hidl_cb) {
364     _hidl_cb(FAILURE(Result::NOT_SUPPORTED), {});
365     return Void();
366 }
367 
getLatency()368 Return<uint32_t> StreamOut::getLatency() {
369     const int latencyMs = DevicePortSink::getLatencyMs(getDeviceAddress(), getAudioConfig());
370 
371     return (latencyMs >= 0) ? latencyMs :
372         (mCommon.getFrameCount() * 1000 / mCommon.getSampleRate());
373 }
374 
setVolume(float left,float right)375 Return<Result> StreamOut::setVolume(float left, float right) {
376     if (isnan(left) || left < 0.0f || left > 1.0f
377         || right < 0.0f || right > 1.0f || isnan(right)) {
378         return FAILURE(Result::INVALID_ARGUMENTS);
379     }
380 
381     std::lock_guard<std::mutex> guard(mMutex);
382     mStreamVolume = (left + right) / 2.0f;
383     updateEffectiveVolumeLocked();
384     return Result::OK;
385 }
386 
updateSourceMetadata(const SourceMetadata & sourceMetadata)387 Return<Result> StreamOut::updateSourceMetadata(const SourceMetadata& sourceMetadata) {
388     (void)sourceMetadata;
389     return Result::NOT_SUPPORTED;
390 }
391 
prepareForWriting(uint32_t frameSize,uint32_t framesCount,prepareForWriting_cb _hidl_cb)392 Return<void> StreamOut::prepareForWriting(uint32_t frameSize,
393                                           uint32_t framesCount,
394                                           prepareForWriting_cb _hidl_cb) {
395     if (!frameSize || !framesCount || frameSize > 256 || framesCount > (1u << 20)) {
396         _hidl_cb(FAILURE(Result::INVALID_ARGUMENTS), {}, {}, {}, -1);
397         return Void();
398     }
399 
400     if (mWriteThread) {  // INVALID_STATE if the method was already called.
401         _hidl_cb(FAILURE(Result::INVALID_STATE), {}, {}, {}, -1);
402         return Void();
403     }
404 
405     auto t = std::make_unique<WriteThread>(this, frameSize * framesCount);
406 
407     if (t->isRunning()) {
408         _hidl_cb(Result::OK,
409                  *(t->mCommandMQ.getDesc()),
410                  *(t->mDataMQ.getDesc()),
411                  *(t->mStatusMQ.getDesc()),
412                  t->getTid().get());
413 
414         mWriteThread = std::move(t);
415     } else {
416         _hidl_cb(FAILURE(Result::INVALID_ARGUMENTS), {}, {}, {}, -1);
417     }
418 
419     return Void();
420 }
421 
getRenderPosition(getRenderPosition_cb _hidl_cb)422 Return<void> StreamOut::getRenderPosition(getRenderPosition_cb _hidl_cb) {
423     _hidl_cb(FAILURE(Result::NOT_SUPPORTED), 0);
424     return Void();
425 }
426 
getNextWriteTimestamp(getNextWriteTimestamp_cb _hidl_cb)427 Return<void> StreamOut::getNextWriteTimestamp(getNextWriteTimestamp_cb _hidl_cb) {
428     _hidl_cb(FAILURE(Result::NOT_SUPPORTED), 0);
429     return Void();
430 }
431 
setCallback(const sp<IStreamOutCallback> & callback)432 Return<Result> StreamOut::setCallback(const sp<IStreamOutCallback>& callback) {
433     (void)callback;
434     return FAILURE(Result::NOT_SUPPORTED);
435 }
436 
clearCallback()437 Return<Result> StreamOut::clearCallback() {
438     return FAILURE(Result::NOT_SUPPORTED);
439 }
440 
setEventCallback(const sp<IStreamOutEventCallback> & callback)441 Return<Result> StreamOut::setEventCallback(const sp<IStreamOutEventCallback>& callback) {
442     (void)callback;
443     return Result::NOT_SUPPORTED;
444 }
445 
supportsPauseAndResume(supportsPauseAndResume_cb _hidl_cb)446 Return<void> StreamOut::supportsPauseAndResume(supportsPauseAndResume_cb _hidl_cb) {
447     _hidl_cb(false, false);
448     return Void();
449 }
450 
pause()451 Return<Result> StreamOut::pause() {
452     return FAILURE(Result::NOT_SUPPORTED);
453 }
454 
resume()455 Return<Result> StreamOut::resume() {
456     return FAILURE(Result::NOT_SUPPORTED);
457 }
458 
supportsDrain()459 Return<bool> StreamOut::supportsDrain() {
460     return false;
461 }
462 
drain(AudioDrain type)463 Return<Result> StreamOut::drain(AudioDrain type) {
464     (void)type;
465     return FAILURE(Result::NOT_SUPPORTED);
466 }
467 
flush()468 Return<Result> StreamOut::flush() {
469     return FAILURE(Result::NOT_SUPPORTED);
470 }
471 
getPresentationPosition(getPresentationPosition_cb _hidl_cb)472 Return<void> StreamOut::getPresentationPosition(getPresentationPosition_cb _hidl_cb) {
473     const auto w = static_cast<WriteThread*>(mWriteThread.get());
474     if (!w) {
475         _hidl_cb(FAILURE(Result::INVALID_STATE), {}, {});
476         return Void();
477     }
478 
479     const auto s = w->mSink.get();
480     if (!s) {
481         _hidl_cb(Result::OK, mFrames, util::nsecs2TimeSpec(systemTime(SYSTEM_TIME_MONOTONIC)));
482     } else {
483         uint64_t frames;
484         TimeSpec ts;
485         const Result r = s->getPresentationPosition(frames, ts);
486         _hidl_cb(r, frames, ts);
487     }
488 
489     return Void();
490 }
491 
selectPresentation(int32_t presentationId,int32_t programId)492 Return<Result> StreamOut::selectPresentation(int32_t presentationId,
493                                              int32_t programId) {
494     (void)presentationId;
495     (void)programId;
496     return FAILURE(Result::NOT_SUPPORTED);
497 }
498 
getDualMonoMode(getDualMonoMode_cb _hidl_cb)499 Return<void> StreamOut::getDualMonoMode(getDualMonoMode_cb _hidl_cb) {
500     _hidl_cb(FAILURE(Result::NOT_SUPPORTED), {});
501     return Void();
502 }
503 
setDualMonoMode(DualMonoMode mode)504 Return<Result> StreamOut::setDualMonoMode(DualMonoMode mode) {
505     (void)mode;
506     return FAILURE(Result::NOT_SUPPORTED);
507 }
508 
getAudioDescriptionMixLevel(getAudioDescriptionMixLevel_cb _hidl_cb)509 Return<void> StreamOut::getAudioDescriptionMixLevel(getAudioDescriptionMixLevel_cb _hidl_cb) {
510     _hidl_cb(FAILURE(Result::NOT_SUPPORTED), 0);
511     return Void();
512 }
513 
setAudioDescriptionMixLevel(float leveldB)514 Return<Result> StreamOut::setAudioDescriptionMixLevel(float leveldB) {
515     (void)leveldB;
516     return FAILURE(Result::NOT_SUPPORTED);
517 }
518 
getPlaybackRateParameters(getPlaybackRateParameters_cb _hidl_cb)519 Return<void> StreamOut::getPlaybackRateParameters(getPlaybackRateParameters_cb _hidl_cb) {
520     _hidl_cb(FAILURE(Result::NOT_SUPPORTED), {});
521     return Void();
522 }
523 
setPlaybackRateParameters(const PlaybackRate & playbackRate)524 Return<Result> StreamOut::setPlaybackRateParameters(const PlaybackRate &playbackRate) {
525     (void)playbackRate;
526     return FAILURE(Result::NOT_SUPPORTED);
527 }
528 
529 #if MAJOR_VERSION == 7 && MINOR_VERSION == 1
setLatencyMode(LatencyMode mode __unused)530 Return<Result> StreamOut::setLatencyMode(LatencyMode mode __unused) {
531     return FAILURE(Result::NOT_SUPPORTED);
532 };
533 
getRecommendedLatencyModes(getRecommendedLatencyModes_cb _hidl_cb)534 Return<void> StreamOut::getRecommendedLatencyModes(getRecommendedLatencyModes_cb _hidl_cb) {
535     hidl_vec<LatencyMode> hidlModes;
536     _hidl_cb(Result::NOT_SUPPORTED, hidlModes);
537     return Void();
538 };
539 
setLatencyModeCallback(const sp<IStreamOutLatencyModeCallback> & callback __unused)540 Return<Result> StreamOut::setLatencyModeCallback(
541         const sp<IStreamOutLatencyModeCallback>& callback __unused) {
542     return FAILURE(Result::NOT_SUPPORTED);
543 };
544 #endif
545 
setMasterVolume(float masterVolume)546 void StreamOut::setMasterVolume(float masterVolume) {
547     std::lock_guard<std::mutex> guard(mMutex);
548     mMasterVolume = masterVolume;
549     updateEffectiveVolumeLocked();
550 }
551 
updateEffectiveVolumeLocked()552 void StreamOut::updateEffectiveVolumeLocked() {
553     mEffectiveVolume = mMasterVolume * mStreamVolume;
554 }
555 
validateDeviceAddress(const DeviceAddress & device)556 bool StreamOut::validateDeviceAddress(const DeviceAddress& device) {
557     return DevicePortSink::validateDeviceAddress(device);
558 }
559 
validateFlags(const hidl_vec<AudioInOutFlag> & flags)560 bool StreamOut::validateFlags(const hidl_vec<AudioInOutFlag>& flags) {
561     return std::all_of(flags.begin(), flags.end(), [](const AudioInOutFlag& flag){
562         return xsd::stringToAudioInOutFlag(flag) != xsd::AudioInOutFlag::UNKNOWN;
563     });
564 }
565 
validateSourceMetadata(const SourceMetadata & sourceMetadata)566 bool StreamOut::validateSourceMetadata(const SourceMetadata& sourceMetadata) {
567     for (const auto& track : sourceMetadata.tracks) {
568         if (xsd::isUnknownAudioUsage(track.usage)
569                 || xsd::isUnknownAudioContentType(track.contentType)
570                 || xsd::isUnknownAudioChannelMask(track.channelMask)) {
571             return false;
572         }
573         for (const auto& tag : track.tags) {
574             if (!xsd::isVendorExtension(tag)) {
575                 return false;
576             }
577         }
578     }
579     return true;
580 }
581 
582 }  // namespace implementation
583 }  // namespace CPP_VERSION
584 }  // namespace audio
585 }  // namespace hardware
586 }  // namespace android
587