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