• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 <pthread.h>
18 
19 #define ATRACE_TAG ATRACE_TAG_AUDIO
20 #define LOG_TAG "AHAL_Stream"
21 #include <Utils.h>
22 #include <android-base/logging.h>
23 #include <android/binder_ibinder_platform.h>
24 #include <cutils/properties.h>
25 #include <utils/SystemClock.h>
26 #include <utils/Trace.h>
27 
28 #include "core-impl/Stream.h"
29 
30 using aidl::android::hardware::audio::common::AudioOffloadMetadata;
31 using aidl::android::hardware::audio::common::getChannelCount;
32 using aidl::android::hardware::audio::common::getFrameSizeInBytes;
33 using aidl::android::hardware::audio::common::isBitPositionFlagSet;
34 using aidl::android::hardware::audio::common::SinkMetadata;
35 using aidl::android::hardware::audio::common::SourceMetadata;
36 using aidl::android::media::audio::common::AudioDevice;
37 using aidl::android::media::audio::common::AudioDualMonoMode;
38 using aidl::android::media::audio::common::AudioInputFlags;
39 using aidl::android::media::audio::common::AudioIoFlags;
40 using aidl::android::media::audio::common::AudioLatencyMode;
41 using aidl::android::media::audio::common::AudioOffloadInfo;
42 using aidl::android::media::audio::common::AudioOutputFlags;
43 using aidl::android::media::audio::common::AudioPlaybackRate;
44 using aidl::android::media::audio::common::MicrophoneDynamicInfo;
45 using aidl::android::media::audio::common::MicrophoneInfo;
46 
47 namespace aidl::android::hardware::audio::core {
48 
fillDescriptor(StreamDescriptor * desc)49 void StreamContext::fillDescriptor(StreamDescriptor* desc) {
50     if (mCommandMQ) {
51         desc->command = mCommandMQ->dupeDesc();
52     }
53     if (mReplyMQ) {
54         desc->reply = mReplyMQ->dupeDesc();
55     }
56     if (mDataMQ) {
57         desc->frameSizeBytes = getFrameSize();
58         desc->bufferSizeFrames = getBufferSizeInFrames();
59         desc->audio.set<StreamDescriptor::AudioBuffer::Tag::fmq>(mDataMQ->dupeDesc());
60     }
61 }
62 
getBufferSizeInFrames() const63 size_t StreamContext::getBufferSizeInFrames() const {
64     if (mDataMQ) {
65         return mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize() / getFrameSize();
66     }
67     return 0;
68 }
69 
getFrameSize() const70 size_t StreamContext::getFrameSize() const {
71     return getFrameSizeInBytes(mFormat, mChannelLayout);
72 }
73 
isValid() const74 bool StreamContext::isValid() const {
75     if (mCommandMQ && !mCommandMQ->isValid()) {
76         LOG(ERROR) << "command FMQ is invalid";
77         return false;
78     }
79     if (mReplyMQ && !mReplyMQ->isValid()) {
80         LOG(ERROR) << "reply FMQ is invalid";
81         return false;
82     }
83     if (getFrameSize() == 0) {
84         LOG(ERROR) << "frame size is invalid";
85         return false;
86     }
87     if (mDataMQ && !mDataMQ->isValid()) {
88         LOG(ERROR) << "data FMQ is invalid";
89         return false;
90     }
91     return true;
92 }
93 
startStreamDataProcessor()94 void StreamContext::startStreamDataProcessor() {
95     auto streamDataProcessor = mStreamDataProcessor.lock();
96     if (streamDataProcessor != nullptr) {
97         streamDataProcessor->startDataProcessor(mSampleRate, getChannelCount(mChannelLayout),
98                                                 mFormat);
99     }
100 }
101 
reset()102 void StreamContext::reset() {
103     mCommandMQ.reset();
104     mReplyMQ.reset();
105     mDataMQ.reset();
106 }
107 
getTid() const108 pid_t StreamWorkerCommonLogic::getTid() const {
109 #if defined(__ANDROID__)
110     return pthread_gettid_np(pthread_self());
111 #else
112     return 0;
113 #endif
114 }
115 
init()116 std::string StreamWorkerCommonLogic::init() {
117     if (mContext->getCommandMQ() == nullptr) return "Command MQ is null";
118     if (mContext->getReplyMQ() == nullptr) return "Reply MQ is null";
119     StreamContext::DataMQ* const dataMQ = mContext->getDataMQ();
120     if (dataMQ == nullptr) return "Data MQ is null";
121     if (sizeof(DataBufferElement) != dataMQ->getQuantumSize()) {
122         return "Unexpected Data MQ quantum size: " + std::to_string(dataMQ->getQuantumSize());
123     }
124     mDataBufferSize = dataMQ->getQuantumCount() * dataMQ->getQuantumSize();
125     mDataBuffer.reset(new (std::nothrow) DataBufferElement[mDataBufferSize]);
126     if (mDataBuffer == nullptr) {
127         return "Failed to allocate data buffer for element count " +
128                std::to_string(dataMQ->getQuantumCount()) +
129                ", size in bytes: " + std::to_string(mDataBufferSize);
130     }
131     if (::android::status_t status = mDriver->init(); status != STATUS_OK) {
132         return "Failed to initialize the driver: " + std::to_string(status);
133     }
134     return "";
135 }
136 
populateReply(StreamDescriptor::Reply * reply,bool isConnected) const137 void StreamWorkerCommonLogic::populateReply(StreamDescriptor::Reply* reply,
138                                             bool isConnected) const {
139     reply->status = STATUS_OK;
140     if (isConnected) {
141         reply->observable.frames = mContext->getFrameCount();
142         reply->observable.timeNs = ::android::uptimeNanos();
143         if (auto status = mDriver->refinePosition(&reply->observable); status == ::android::OK) {
144             return;
145         }
146     }
147     reply->observable.frames = StreamDescriptor::Position::UNKNOWN;
148     reply->observable.timeNs = StreamDescriptor::Position::UNKNOWN;
149 }
150 
populateReplyWrongState(StreamDescriptor::Reply * reply,const StreamDescriptor::Command & command) const151 void StreamWorkerCommonLogic::populateReplyWrongState(
152         StreamDescriptor::Reply* reply, const StreamDescriptor::Command& command) const {
153     LOG(WARNING) << "command '" << toString(command.getTag())
154                  << "' can not be handled in the state " << toString(mState);
155     reply->status = STATUS_INVALID_OPERATION;
156 }
157 
158 const std::string StreamInWorkerLogic::kThreadName = "reader";
159 
cycle()160 StreamInWorkerLogic::Status StreamInWorkerLogic::cycle() {
161     // Note: for input streams, draining is driven by the client, thus
162     // "empty buffer" condition can only happen while handling the 'burst'
163     // command. Thus, unlike for output streams, it does not make sense to
164     // delay the 'DRAINING' state here by 'mTransientStateDelayMs'.
165     // TODO: Add a delay for transitions of async operations when/if they added.
166 
167     StreamDescriptor::Command command{};
168     if (!mContext->getCommandMQ()->readBlocking(&command, 1)) {
169         LOG(ERROR) << __func__ << ": reading of command from MQ failed";
170         mState = StreamDescriptor::State::ERROR;
171         return Status::ABORT;
172     }
173     using Tag = StreamDescriptor::Command::Tag;
174     using LogSeverity = ::android::base::LogSeverity;
175     const LogSeverity severity =
176             command.getTag() == Tag::burst || command.getTag() == Tag::getStatus
177                     ? LogSeverity::VERBOSE
178                     : LogSeverity::DEBUG;
179     LOG(severity) << __func__ << ": received command " << command.toString() << " in "
180                   << kThreadName;
181     StreamDescriptor::Reply reply{};
182     reply.status = STATUS_BAD_VALUE;
183     switch (command.getTag()) {
184         case Tag::halReservedExit: {
185             const int32_t cookie = command.get<Tag::halReservedExit>();
186             StreamInWorkerLogic::Status status = Status::CONTINUE;
187             if (cookie == (mContext->getInternalCommandCookie() ^ getTid())) {
188                 mDriver->shutdown();
189                 setClosed();
190                 status = Status::EXIT;
191             } else {
192                 LOG(WARNING) << __func__ << ": EXIT command has a bad cookie: " << cookie;
193             }
194             if (cookie != 0) {  // This is an internal command, no need to reply.
195                 return status;
196             }
197             // `cookie == 0` can only occur in the context of a VTS test, need to reply.
198             break;
199         }
200         case Tag::getStatus:
201             populateReply(&reply, mIsConnected);
202             break;
203         case Tag::start:
204             if (mState == StreamDescriptor::State::STANDBY ||
205                 mState == StreamDescriptor::State::DRAINING) {
206                 if (::android::status_t status = mDriver->start(); status == ::android::OK) {
207                     populateReply(&reply, mIsConnected);
208                     mState = mState == StreamDescriptor::State::STANDBY
209                                      ? StreamDescriptor::State::IDLE
210                                      : StreamDescriptor::State::ACTIVE;
211                 } else {
212                     LOG(ERROR) << __func__ << ": start failed: " << status;
213                     mState = StreamDescriptor::State::ERROR;
214                 }
215             } else {
216                 populateReplyWrongState(&reply, command);
217             }
218             break;
219         case Tag::burst:
220             if (const int32_t fmqByteCount = command.get<Tag::burst>(); fmqByteCount >= 0) {
221                 LOG(VERBOSE) << __func__ << ": '" << toString(command.getTag()) << "' command for "
222                              << fmqByteCount << " bytes";
223                 if (mState == StreamDescriptor::State::IDLE ||
224                     mState == StreamDescriptor::State::ACTIVE ||
225                     mState == StreamDescriptor::State::PAUSED ||
226                     mState == StreamDescriptor::State::DRAINING) {
227                     if (!read(fmqByteCount, &reply)) {
228                         mState = StreamDescriptor::State::ERROR;
229                     }
230                     if (mState == StreamDescriptor::State::IDLE ||
231                         mState == StreamDescriptor::State::PAUSED) {
232                         mState = StreamDescriptor::State::ACTIVE;
233                     } else if (mState == StreamDescriptor::State::DRAINING) {
234                         // To simplify the reference code, we assume that the read operation
235                         // has consumed all the data remaining in the hardware buffer.
236                         // In a real implementation, here we would either remain in
237                         // the 'DRAINING' state, or transfer to 'STANDBY' depending on the
238                         // buffer state.
239                         mState = StreamDescriptor::State::STANDBY;
240                     }
241                 } else {
242                     populateReplyWrongState(&reply, command);
243                 }
244             } else {
245                 LOG(WARNING) << __func__ << ": invalid burst byte count: " << fmqByteCount;
246             }
247             break;
248         case Tag::drain:
249             if (const auto mode = command.get<Tag::drain>();
250                 mode == StreamDescriptor::DrainMode::DRAIN_UNSPECIFIED) {
251                 if (mState == StreamDescriptor::State::ACTIVE) {
252                     if (::android::status_t status = mDriver->drain(mode);
253                         status == ::android::OK) {
254                         populateReply(&reply, mIsConnected);
255                         mState = StreamDescriptor::State::DRAINING;
256                     } else {
257                         LOG(ERROR) << __func__ << ": drain failed: " << status;
258                         mState = StreamDescriptor::State::ERROR;
259                     }
260                 } else {
261                     populateReplyWrongState(&reply, command);
262                 }
263             } else {
264                 LOG(WARNING) << __func__ << ": invalid drain mode: " << toString(mode);
265             }
266             break;
267         case Tag::standby:
268             if (mState == StreamDescriptor::State::IDLE) {
269                 populateReply(&reply, mIsConnected);
270                 if (::android::status_t status = mDriver->standby(); status == ::android::OK) {
271                     mState = StreamDescriptor::State::STANDBY;
272                 } else {
273                     LOG(ERROR) << __func__ << ": standby failed: " << status;
274                     mState = StreamDescriptor::State::ERROR;
275                 }
276             } else {
277                 populateReplyWrongState(&reply, command);
278             }
279             break;
280         case Tag::pause:
281             if (mState == StreamDescriptor::State::ACTIVE) {
282                 if (::android::status_t status = mDriver->pause(); status == ::android::OK) {
283                     populateReply(&reply, mIsConnected);
284                     mState = StreamDescriptor::State::PAUSED;
285                 } else {
286                     LOG(ERROR) << __func__ << ": pause failed: " << status;
287                     mState = StreamDescriptor::State::ERROR;
288                 }
289             } else {
290                 populateReplyWrongState(&reply, command);
291             }
292             break;
293         case Tag::flush:
294             if (mState == StreamDescriptor::State::PAUSED) {
295                 if (::android::status_t status = mDriver->flush(); status == ::android::OK) {
296                     populateReply(&reply, mIsConnected);
297                     mState = StreamDescriptor::State::STANDBY;
298                 } else {
299                     LOG(ERROR) << __func__ << ": flush failed: " << status;
300                     mState = StreamDescriptor::State::ERROR;
301                 }
302             } else {
303                 populateReplyWrongState(&reply, command);
304             }
305             break;
306     }
307     reply.state = mState;
308     LOG(severity) << __func__ << ": writing reply " << reply.toString();
309     if (!mContext->getReplyMQ()->writeBlocking(&reply, 1)) {
310         LOG(ERROR) << __func__ << ": writing of reply " << reply.toString() << " to MQ failed";
311         mState = StreamDescriptor::State::ERROR;
312         return Status::ABORT;
313     }
314     return Status::CONTINUE;
315 }
316 
read(size_t clientSize,StreamDescriptor::Reply * reply)317 bool StreamInWorkerLogic::read(size_t clientSize, StreamDescriptor::Reply* reply) {
318     ATRACE_CALL();
319     StreamContext::DataMQ* const dataMQ = mContext->getDataMQ();
320     StreamContext::DataMQ::Error fmqError = StreamContext::DataMQ::Error::NONE;
321     std::string fmqErrorMsg;
322     const size_t byteCount = std::min(
323             {clientSize, dataMQ->availableToWrite(&fmqError, &fmqErrorMsg), mDataBufferSize});
324     CHECK(fmqError == StreamContext::DataMQ::Error::NONE) << fmqErrorMsg;
325     const bool isConnected = mIsConnected;
326     const size_t frameSize = mContext->getFrameSize();
327     size_t actualFrameCount = 0;
328     bool fatal = false;
329     int32_t latency = mContext->getNominalLatencyMs();
330     if (isConnected) {
331         if (::android::status_t status = mDriver->transfer(mDataBuffer.get(), byteCount / frameSize,
332                                                            &actualFrameCount, &latency);
333             status != ::android::OK) {
334             fatal = true;
335             LOG(ERROR) << __func__ << ": read failed: " << status;
336         }
337     } else {
338         usleep(3000);  // Simulate blocking transfer delay.
339         for (size_t i = 0; i < byteCount; ++i) mDataBuffer[i] = 0;
340         actualFrameCount = byteCount / frameSize;
341     }
342     const size_t actualByteCount = actualFrameCount * frameSize;
343     if (bool success = actualByteCount > 0 ? dataMQ->write(&mDataBuffer[0], actualByteCount) : true;
344         success) {
345         LOG(VERBOSE) << __func__ << ": writing of " << actualByteCount << " bytes into data MQ"
346                      << " succeeded; connected? " << isConnected;
347         // Frames are provided and counted regardless of connection status.
348         reply->fmqByteCount += actualByteCount;
349         mContext->advanceFrameCount(actualFrameCount);
350         populateReply(reply, isConnected);
351     } else {
352         LOG(WARNING) << __func__ << ": writing of " << actualByteCount
353                      << " bytes of data to MQ failed";
354         reply->status = STATUS_NOT_ENOUGH_DATA;
355     }
356     reply->latencyMs = latency;
357     return !fatal;
358 }
359 
360 const std::string StreamOutWorkerLogic::kThreadName = "writer";
361 
cycle()362 StreamOutWorkerLogic::Status StreamOutWorkerLogic::cycle() {
363     if (mState == StreamDescriptor::State::DRAINING ||
364         mState == StreamDescriptor::State::TRANSFERRING) {
365         if (auto stateDurationMs = std::chrono::duration_cast<std::chrono::milliseconds>(
366                     std::chrono::steady_clock::now() - mTransientStateStart);
367             stateDurationMs >= mTransientStateDelayMs) {
368             std::shared_ptr<IStreamCallback> asyncCallback = mContext->getAsyncCallback();
369             if (asyncCallback == nullptr) {
370                 // In blocking mode, mState can only be DRAINING.
371                 mState = StreamDescriptor::State::IDLE;
372             } else {
373                 // In a real implementation, the driver should notify the HAL about
374                 // drain or transfer completion. In the stub, we switch unconditionally.
375                 if (mState == StreamDescriptor::State::DRAINING) {
376                     mState = StreamDescriptor::State::IDLE;
377                     ndk::ScopedAStatus status = asyncCallback->onDrainReady();
378                     if (!status.isOk()) {
379                         LOG(ERROR) << __func__ << ": error from onDrainReady: " << status;
380                     }
381                 } else {
382                     mState = StreamDescriptor::State::ACTIVE;
383                     ndk::ScopedAStatus status = asyncCallback->onTransferReady();
384                     if (!status.isOk()) {
385                         LOG(ERROR) << __func__ << ": error from onTransferReady: " << status;
386                     }
387                 }
388             }
389             if (mTransientStateDelayMs.count() != 0) {
390                 LOG(DEBUG) << __func__ << ": switched to state " << toString(mState)
391                            << " after a timeout";
392             }
393         }
394     }
395 
396     StreamDescriptor::Command command{};
397     if (!mContext->getCommandMQ()->readBlocking(&command, 1)) {
398         LOG(ERROR) << __func__ << ": reading of command from MQ failed";
399         mState = StreamDescriptor::State::ERROR;
400         return Status::ABORT;
401     }
402     using Tag = StreamDescriptor::Command::Tag;
403     using LogSeverity = ::android::base::LogSeverity;
404     const LogSeverity severity =
405             command.getTag() == Tag::burst || command.getTag() == Tag::getStatus
406                     ? LogSeverity::VERBOSE
407                     : LogSeverity::DEBUG;
408     LOG(severity) << __func__ << ": received command " << command.toString() << " in "
409                   << kThreadName;
410     StreamDescriptor::Reply reply{};
411     reply.status = STATUS_BAD_VALUE;
412     using Tag = StreamDescriptor::Command::Tag;
413     switch (command.getTag()) {
414         case Tag::halReservedExit: {
415             const int32_t cookie = command.get<Tag::halReservedExit>();
416             StreamOutWorkerLogic::Status status = Status::CONTINUE;
417             if (cookie == (mContext->getInternalCommandCookie() ^ getTid())) {
418                 mDriver->shutdown();
419                 setClosed();
420                 status = Status::EXIT;
421             } else {
422                 LOG(WARNING) << __func__ << ": EXIT command has a bad cookie: " << cookie;
423             }
424             if (cookie != 0) {  // This is an internal command, no need to reply.
425                 return status;
426             }
427             // `cookie == 0` can only occur in the context of a VTS test, need to reply.
428             break;
429         }
430         case Tag::getStatus:
431             populateReply(&reply, mIsConnected);
432             break;
433         case Tag::start: {
434             std::optional<StreamDescriptor::State> nextState;
435             switch (mState) {
436                 case StreamDescriptor::State::STANDBY:
437                     nextState = StreamDescriptor::State::IDLE;
438                     break;
439                 case StreamDescriptor::State::PAUSED:
440                     nextState = StreamDescriptor::State::ACTIVE;
441                     break;
442                 case StreamDescriptor::State::DRAIN_PAUSED:
443                     nextState = StreamDescriptor::State::DRAINING;
444                     break;
445                 case StreamDescriptor::State::TRANSFER_PAUSED:
446                     nextState = StreamDescriptor::State::TRANSFERRING;
447                     break;
448                 default:
449                     populateReplyWrongState(&reply, command);
450             }
451             if (nextState.has_value()) {
452                 if (::android::status_t status = mDriver->start(); status == ::android::OK) {
453                     populateReply(&reply, mIsConnected);
454                     if (*nextState == StreamDescriptor::State::IDLE ||
455                         *nextState == StreamDescriptor::State::ACTIVE) {
456                         mState = *nextState;
457                     } else {
458                         switchToTransientState(*nextState);
459                     }
460                 } else {
461                     LOG(ERROR) << __func__ << ": start failed: " << status;
462                     mState = StreamDescriptor::State::ERROR;
463                 }
464             }
465         } break;
466         case Tag::burst:
467             if (const int32_t fmqByteCount = command.get<Tag::burst>(); fmqByteCount >= 0) {
468                 LOG(VERBOSE) << __func__ << ": '" << toString(command.getTag()) << "' command for "
469                              << fmqByteCount << " bytes";
470                 if (mState != StreamDescriptor::State::ERROR &&
471                     mState != StreamDescriptor::State::TRANSFERRING &&
472                     mState != StreamDescriptor::State::TRANSFER_PAUSED) {
473                     if (!write(fmqByteCount, &reply)) {
474                         mState = StreamDescriptor::State::ERROR;
475                     }
476                     std::shared_ptr<IStreamCallback> asyncCallback = mContext->getAsyncCallback();
477                     if (mState == StreamDescriptor::State::STANDBY ||
478                         mState == StreamDescriptor::State::DRAIN_PAUSED ||
479                         mState == StreamDescriptor::State::PAUSED) {
480                         if (asyncCallback == nullptr ||
481                             mState != StreamDescriptor::State::DRAIN_PAUSED) {
482                             mState = StreamDescriptor::State::PAUSED;
483                         } else {
484                             mState = StreamDescriptor::State::TRANSFER_PAUSED;
485                         }
486                     } else if (mState == StreamDescriptor::State::IDLE ||
487                                mState == StreamDescriptor::State::DRAINING ||
488                                mState == StreamDescriptor::State::ACTIVE) {
489                         if (asyncCallback == nullptr || reply.fmqByteCount == fmqByteCount) {
490                             mState = StreamDescriptor::State::ACTIVE;
491                         } else {
492                             switchToTransientState(StreamDescriptor::State::TRANSFERRING);
493                         }
494                     }
495                 } else {
496                     populateReplyWrongState(&reply, command);
497                 }
498             } else {
499                 LOG(WARNING) << __func__ << ": invalid burst byte count: " << fmqByteCount;
500             }
501             break;
502         case Tag::drain:
503             if (const auto mode = command.get<Tag::drain>();
504                 mode == StreamDescriptor::DrainMode::DRAIN_ALL ||
505                 mode == StreamDescriptor::DrainMode::DRAIN_EARLY_NOTIFY) {
506                 if (mState == StreamDescriptor::State::ACTIVE ||
507                     mState == StreamDescriptor::State::TRANSFERRING) {
508                     if (::android::status_t status = mDriver->drain(mode);
509                         status == ::android::OK) {
510                         populateReply(&reply, mIsConnected);
511                         if (mState == StreamDescriptor::State::ACTIVE &&
512                             mContext->getForceSynchronousDrain()) {
513                             mState = StreamDescriptor::State::IDLE;
514                         } else {
515                             switchToTransientState(StreamDescriptor::State::DRAINING);
516                         }
517                     } else {
518                         LOG(ERROR) << __func__ << ": drain failed: " << status;
519                         mState = StreamDescriptor::State::ERROR;
520                     }
521                 } else if (mState == StreamDescriptor::State::TRANSFER_PAUSED) {
522                     mState = StreamDescriptor::State::DRAIN_PAUSED;
523                     populateReply(&reply, mIsConnected);
524                 } else {
525                     populateReplyWrongState(&reply, command);
526                 }
527             } else {
528                 LOG(WARNING) << __func__ << ": invalid drain mode: " << toString(mode);
529             }
530             break;
531         case Tag::standby:
532             if (mState == StreamDescriptor::State::IDLE) {
533                 populateReply(&reply, mIsConnected);
534                 if (::android::status_t status = mDriver->standby(); status == ::android::OK) {
535                     mState = StreamDescriptor::State::STANDBY;
536                 } else {
537                     LOG(ERROR) << __func__ << ": standby failed: " << status;
538                     mState = StreamDescriptor::State::ERROR;
539                 }
540             } else {
541                 populateReplyWrongState(&reply, command);
542             }
543             break;
544         case Tag::pause: {
545             std::optional<StreamDescriptor::State> nextState;
546             switch (mState) {
547                 case StreamDescriptor::State::ACTIVE:
548                     nextState = StreamDescriptor::State::PAUSED;
549                     break;
550                 case StreamDescriptor::State::DRAINING:
551                     nextState = StreamDescriptor::State::DRAIN_PAUSED;
552                     break;
553                 case StreamDescriptor::State::TRANSFERRING:
554                     nextState = StreamDescriptor::State::TRANSFER_PAUSED;
555                     break;
556                 default:
557                     populateReplyWrongState(&reply, command);
558             }
559             if (nextState.has_value()) {
560                 if (::android::status_t status = mDriver->pause(); status == ::android::OK) {
561                     populateReply(&reply, mIsConnected);
562                     mState = nextState.value();
563                 } else {
564                     LOG(ERROR) << __func__ << ": pause failed: " << status;
565                     mState = StreamDescriptor::State::ERROR;
566                 }
567             }
568         } break;
569         case Tag::flush:
570             if (mState == StreamDescriptor::State::PAUSED ||
571                 mState == StreamDescriptor::State::DRAIN_PAUSED ||
572                 mState == StreamDescriptor::State::TRANSFER_PAUSED) {
573                 if (::android::status_t status = mDriver->flush(); status == ::android::OK) {
574                     populateReply(&reply, mIsConnected);
575                     mState = StreamDescriptor::State::IDLE;
576                 } else {
577                     LOG(ERROR) << __func__ << ": flush failed: " << status;
578                     mState = StreamDescriptor::State::ERROR;
579                 }
580             } else {
581                 populateReplyWrongState(&reply, command);
582             }
583             break;
584     }
585     reply.state = mState;
586     LOG(severity) << __func__ << ": writing reply " << reply.toString();
587     if (!mContext->getReplyMQ()->writeBlocking(&reply, 1)) {
588         LOG(ERROR) << __func__ << ": writing of reply " << reply.toString() << " to MQ failed";
589         mState = StreamDescriptor::State::ERROR;
590         return Status::ABORT;
591     }
592     return Status::CONTINUE;
593 }
594 
write(size_t clientSize,StreamDescriptor::Reply * reply)595 bool StreamOutWorkerLogic::write(size_t clientSize, StreamDescriptor::Reply* reply) {
596     ATRACE_CALL();
597     StreamContext::DataMQ* const dataMQ = mContext->getDataMQ();
598     StreamContext::DataMQ::Error fmqError = StreamContext::DataMQ::Error::NONE;
599     std::string fmqErrorMsg;
600     const size_t readByteCount = dataMQ->availableToRead(&fmqError, &fmqErrorMsg);
601     CHECK(fmqError == StreamContext::DataMQ::Error::NONE) << fmqErrorMsg;
602     const size_t frameSize = mContext->getFrameSize();
603     bool fatal = false;
604     int32_t latency = mContext->getNominalLatencyMs();
605     if (readByteCount > 0 ? dataMQ->read(&mDataBuffer[0], readByteCount) : true) {
606         const bool isConnected = mIsConnected;
607         LOG(VERBOSE) << __func__ << ": reading of " << readByteCount << " bytes from data MQ"
608                      << " succeeded; connected? " << isConnected;
609         // Amount of data that the HAL module is going to actually use.
610         size_t byteCount = std::min({clientSize, readByteCount, mDataBufferSize});
611         if (byteCount >= frameSize && mContext->getForceTransientBurst()) {
612             // In order to prevent the state machine from going to ACTIVE state,
613             // simulate partial write.
614             byteCount -= frameSize;
615         }
616         size_t actualFrameCount = 0;
617         if (isConnected) {
618             if (::android::status_t status = mDriver->transfer(
619                         mDataBuffer.get(), byteCount / frameSize, &actualFrameCount, &latency);
620                 status != ::android::OK) {
621                 fatal = true;
622                 LOG(ERROR) << __func__ << ": write failed: " << status;
623             }
624             auto streamDataProcessor = mContext->getStreamDataProcessor().lock();
625             if (streamDataProcessor != nullptr) {
626                 streamDataProcessor->process(mDataBuffer.get(), actualFrameCount * frameSize);
627             }
628         } else {
629             if (mContext->getAsyncCallback() == nullptr) {
630                 usleep(3000);  // Simulate blocking transfer delay.
631             }
632             actualFrameCount = byteCount / frameSize;
633         }
634         const size_t actualByteCount = actualFrameCount * frameSize;
635         // Frames are consumed and counted regardless of the connection status.
636         reply->fmqByteCount += actualByteCount;
637         mContext->advanceFrameCount(actualFrameCount);
638         populateReply(reply, isConnected);
639     } else {
640         LOG(WARNING) << __func__ << ": reading of " << readByteCount
641                      << " bytes of data from MQ failed";
642         reply->status = STATUS_NOT_ENOUGH_DATA;
643     }
644     reply->latencyMs = latency;
645     return !fatal;
646 }
647 
~StreamCommonImpl()648 StreamCommonImpl::~StreamCommonImpl() {
649     if (!isClosed()) {
650         LOG(ERROR) << __func__ << ": stream was not closed prior to destruction, resource leak";
651         stopWorker();
652         // The worker and the context should clean up by themselves via destructors.
653     }
654 }
655 
initInstance(const std::shared_ptr<StreamCommonInterface> & delegate)656 ndk::ScopedAStatus StreamCommonImpl::initInstance(
657         const std::shared_ptr<StreamCommonInterface>& delegate) {
658     mCommon = ndk::SharedRefBase::make<StreamCommonDelegator>(delegate);
659     if (!mWorker->start()) {
660         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
661     }
662     if (auto flags = getContext().getFlags();
663         (flags.getTag() == AudioIoFlags::Tag::input &&
664          isBitPositionFlagSet(flags.template get<AudioIoFlags::Tag::input>(),
665                               AudioInputFlags::FAST)) ||
666         (flags.getTag() == AudioIoFlags::Tag::output &&
667          (isBitPositionFlagSet(flags.template get<AudioIoFlags::Tag::output>(),
668                                AudioOutputFlags::FAST) ||
669           isBitPositionFlagSet(flags.template get<AudioIoFlags::Tag::output>(),
670                                AudioOutputFlags::SPATIALIZER)))) {
671         // FAST workers should be run with a SCHED_FIFO scheduler, however the host process
672         // might be lacking the capability to request it, thus a failure to set is not an error.
673         pid_t workerTid = mWorker->getTid();
674         if (workerTid > 0) {
675             constexpr int32_t kRTPriorityMin = 1;  // SchedulingPolicyService.PRIORITY_MIN (Java).
676             constexpr int32_t kRTPriorityMax = 3;  // SchedulingPolicyService.PRIORITY_MAX (Java).
677             int priorityBoost = kRTPriorityMax;
678             if (flags.getTag() == AudioIoFlags::Tag::output &&
679                 isBitPositionFlagSet(flags.template get<AudioIoFlags::Tag::output>(),
680                                      AudioOutputFlags::SPATIALIZER)) {
681                 const int32_t sptPrio =
682                         property_get_int32("audio.spatializer.priority", kRTPriorityMin);
683                 if (sptPrio >= kRTPriorityMin && sptPrio <= kRTPriorityMax) {
684                     priorityBoost = sptPrio;
685                 } else {
686                     LOG(WARNING) << __func__ << ": invalid spatializer priority: " << sptPrio;
687                     return ndk::ScopedAStatus::ok();
688                 }
689             }
690             struct sched_param param = {
691                     .sched_priority = priorityBoost,
692             };
693             if (sched_setscheduler(workerTid, SCHED_FIFO | SCHED_RESET_ON_FORK, &param) != 0) {
694                 PLOG(WARNING) << __func__ << ": failed to set FIFO scheduler and priority";
695             }
696         } else {
697             LOG(WARNING) << __func__ << ": invalid worker tid: " << workerTid;
698         }
699     }
700     return ndk::ScopedAStatus::ok();
701 }
702 
getStreamCommonCommon(std::shared_ptr<IStreamCommon> * _aidl_return)703 ndk::ScopedAStatus StreamCommonImpl::getStreamCommonCommon(
704         std::shared_ptr<IStreamCommon>* _aidl_return) {
705     if (!mCommon) {
706         LOG(FATAL) << __func__ << ": the common interface was not created";
707     }
708     *_aidl_return = mCommon.getInstance();
709     LOG(DEBUG) << __func__ << ": returning " << _aidl_return->get()->asBinder().get();
710     return ndk::ScopedAStatus::ok();
711 }
712 
updateHwAvSyncId(int32_t in_hwAvSyncId)713 ndk::ScopedAStatus StreamCommonImpl::updateHwAvSyncId(int32_t in_hwAvSyncId) {
714     LOG(DEBUG) << __func__ << ": id " << in_hwAvSyncId;
715     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
716 }
717 
getVendorParameters(const std::vector<std::string> & in_ids,std::vector<VendorParameter> * _aidl_return)718 ndk::ScopedAStatus StreamCommonImpl::getVendorParameters(
719         const std::vector<std::string>& in_ids, std::vector<VendorParameter>* _aidl_return) {
720     LOG(DEBUG) << __func__ << ": id count: " << in_ids.size();
721     (void)_aidl_return;
722     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
723 }
724 
setVendorParameters(const std::vector<VendorParameter> & in_parameters,bool in_async)725 ndk::ScopedAStatus StreamCommonImpl::setVendorParameters(
726         const std::vector<VendorParameter>& in_parameters, bool in_async) {
727     LOG(DEBUG) << __func__ << ": parameters count " << in_parameters.size()
728                << ", async: " << in_async;
729     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
730 }
731 
addEffect(const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect> & in_effect)732 ndk::ScopedAStatus StreamCommonImpl::addEffect(
733         const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect) {
734     if (in_effect == nullptr) {
735         LOG(DEBUG) << __func__ << ": null effect";
736     } else {
737         LOG(DEBUG) << __func__ << ": effect Binder" << in_effect->asBinder().get();
738     }
739     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
740 }
741 
removeEffect(const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect> & in_effect)742 ndk::ScopedAStatus StreamCommonImpl::removeEffect(
743         const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect) {
744     if (in_effect == nullptr) {
745         LOG(DEBUG) << __func__ << ": null effect";
746     } else {
747         LOG(DEBUG) << __func__ << ": effect Binder" << in_effect->asBinder().get();
748     }
749     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
750 }
751 
close()752 ndk::ScopedAStatus StreamCommonImpl::close() {
753     LOG(DEBUG) << __func__;
754     if (!isClosed()) {
755         stopWorker();
756         LOG(DEBUG) << __func__ << ": joining the worker thread...";
757         mWorker->stop();
758         LOG(DEBUG) << __func__ << ": worker thread joined";
759         onClose(mWorker->setClosed());
760         return ndk::ScopedAStatus::ok();
761     } else {
762         LOG(ERROR) << __func__ << ": stream was already closed";
763         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
764     }
765 }
766 
prepareToClose()767 ndk::ScopedAStatus StreamCommonImpl::prepareToClose() {
768     LOG(DEBUG) << __func__;
769     if (!isClosed()) {
770         return ndk::ScopedAStatus::ok();
771     }
772     LOG(ERROR) << __func__ << ": stream was closed";
773     return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
774 }
775 
stopWorker()776 void StreamCommonImpl::stopWorker() {
777     if (auto commandMQ = mContext.getCommandMQ(); commandMQ != nullptr) {
778         LOG(DEBUG) << __func__ << ": asking the worker to exit...";
779         auto cmd = StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::halReservedExit>(
780                 mContext.getInternalCommandCookie() ^ mWorker->getTid());
781         // Note: never call 'pause' and 'resume' methods of StreamWorker
782         // in the HAL implementation. These methods are to be used by
783         // the client side only. Preventing the worker loop from running
784         // on the HAL side can cause a deadlock.
785         if (!commandMQ->writeBlocking(&cmd, 1)) {
786             LOG(ERROR) << __func__ << ": failed to write exit command to the MQ";
787         }
788         LOG(DEBUG) << __func__ << ": done";
789     }
790 }
791 
updateMetadataCommon(const Metadata & metadata)792 ndk::ScopedAStatus StreamCommonImpl::updateMetadataCommon(const Metadata& metadata) {
793     LOG(DEBUG) << __func__;
794     if (!isClosed()) {
795         if (metadata.index() != mMetadata.index()) {
796             LOG(FATAL) << __func__ << ": changing metadata variant is not allowed";
797         }
798         mMetadata = metadata;
799         return ndk::ScopedAStatus::ok();
800     }
801     LOG(ERROR) << __func__ << ": stream was closed";
802     return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
803 }
804 
setConnectedDevices(const std::vector<::aidl::android::media::audio::common::AudioDevice> & devices)805 ndk::ScopedAStatus StreamCommonImpl::setConnectedDevices(
806         const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
807     mWorker->setIsConnected(!devices.empty());
808     mConnectedDevices = devices;
809     return ndk::ScopedAStatus::ok();
810 }
811 
bluetoothParametersUpdated()812 ndk::ScopedAStatus StreamCommonImpl::bluetoothParametersUpdated() {
813     LOG(DEBUG) << __func__;
814     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
815 }
816 
817 namespace {
transformMicrophones(const std::vector<MicrophoneInfo> & microphones)818 static std::map<AudioDevice, std::string> transformMicrophones(
819         const std::vector<MicrophoneInfo>& microphones) {
820     std::map<AudioDevice, std::string> result;
821     std::transform(microphones.begin(), microphones.end(), std::inserter(result, result.begin()),
822                    [](const auto& mic) { return std::make_pair(mic.device, mic.id); });
823     return result;
824 }
825 }  // namespace
826 
StreamIn(StreamContext && context,const std::vector<MicrophoneInfo> & microphones)827 StreamIn::StreamIn(StreamContext&& context, const std::vector<MicrophoneInfo>& microphones)
828     : mContextInstance(std::move(context)), mMicrophones(transformMicrophones(microphones)) {
829     LOG(DEBUG) << __func__;
830 }
831 
defaultOnClose()832 void StreamIn::defaultOnClose() {
833     mContextInstance.reset();
834 }
835 
getActiveMicrophones(std::vector<MicrophoneDynamicInfo> * _aidl_return)836 ndk::ScopedAStatus StreamIn::getActiveMicrophones(
837         std::vector<MicrophoneDynamicInfo>* _aidl_return) {
838     std::vector<MicrophoneDynamicInfo> result;
839     std::vector<MicrophoneDynamicInfo::ChannelMapping> channelMapping{
840             getChannelCount(getContext().getChannelLayout()),
841             MicrophoneDynamicInfo::ChannelMapping::DIRECT};
842     for (auto it = getConnectedDevices().begin(); it != getConnectedDevices().end(); ++it) {
843         if (auto micIt = mMicrophones.find(*it); micIt != mMicrophones.end()) {
844             MicrophoneDynamicInfo dynMic;
845             dynMic.id = micIt->second;
846             dynMic.channelMapping = channelMapping;
847             result.push_back(std::move(dynMic));
848         }
849     }
850     *_aidl_return = std::move(result);
851     LOG(DEBUG) << __func__ << ": returning " << ::android::internal::ToString(*_aidl_return);
852     return ndk::ScopedAStatus::ok();
853 }
854 
getMicrophoneDirection(MicrophoneDirection * _aidl_return)855 ndk::ScopedAStatus StreamIn::getMicrophoneDirection(MicrophoneDirection* _aidl_return) {
856     LOG(DEBUG) << __func__;
857     (void)_aidl_return;
858     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
859 }
860 
setMicrophoneDirection(MicrophoneDirection in_direction)861 ndk::ScopedAStatus StreamIn::setMicrophoneDirection(MicrophoneDirection in_direction) {
862     LOG(DEBUG) << __func__ << ": direction " << toString(in_direction);
863     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
864 }
865 
getMicrophoneFieldDimension(float * _aidl_return)866 ndk::ScopedAStatus StreamIn::getMicrophoneFieldDimension(float* _aidl_return) {
867     LOG(DEBUG) << __func__;
868     (void)_aidl_return;
869     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
870 }
871 
setMicrophoneFieldDimension(float in_zoom)872 ndk::ScopedAStatus StreamIn::setMicrophoneFieldDimension(float in_zoom) {
873     LOG(DEBUG) << __func__ << ": zoom " << in_zoom;
874     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
875 }
876 
getHwGain(std::vector<float> * _aidl_return)877 ndk::ScopedAStatus StreamIn::getHwGain(std::vector<float>* _aidl_return) {
878     LOG(DEBUG) << __func__;
879     (void)_aidl_return;
880     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
881 }
882 
setHwGain(const std::vector<float> & in_channelGains)883 ndk::ScopedAStatus StreamIn::setHwGain(const std::vector<float>& in_channelGains) {
884     LOG(DEBUG) << __func__ << ": gains " << ::android::internal::ToString(in_channelGains);
885     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
886 }
887 
StreamInHwGainHelper(const StreamContext * context)888 StreamInHwGainHelper::StreamInHwGainHelper(const StreamContext* context)
889     : mChannelCount(getChannelCount(context->getChannelLayout())), mHwGains(mChannelCount, 0.0f) {}
890 
getHwGainImpl(std::vector<float> * _aidl_return)891 ndk::ScopedAStatus StreamInHwGainHelper::getHwGainImpl(std::vector<float>* _aidl_return) {
892     *_aidl_return = mHwGains;
893     LOG(DEBUG) << __func__ << ": returning " << ::android::internal::ToString(*_aidl_return);
894     return ndk::ScopedAStatus::ok();
895 }
896 
setHwGainImpl(const std::vector<float> & in_channelGains)897 ndk::ScopedAStatus StreamInHwGainHelper::setHwGainImpl(const std::vector<float>& in_channelGains) {
898     LOG(DEBUG) << __func__ << ": gains " << ::android::internal::ToString(in_channelGains);
899     if (in_channelGains.size() != mChannelCount) {
900         LOG(ERROR) << __func__
901                    << ": channel count does not match stream channel count: " << mChannelCount;
902         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
903     }
904     for (float gain : in_channelGains) {
905         if (gain < StreamIn::HW_GAIN_MIN || gain > StreamIn::HW_GAIN_MAX) {
906             LOG(ERROR) << __func__ << ": gain value out of range: " << gain;
907             return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
908         }
909     }
910     mHwGains = in_channelGains;
911     return ndk::ScopedAStatus::ok();
912 }
913 
StreamOut(StreamContext && context,const std::optional<AudioOffloadInfo> & offloadInfo)914 StreamOut::StreamOut(StreamContext&& context, const std::optional<AudioOffloadInfo>& offloadInfo)
915     : mContextInstance(std::move(context)), mOffloadInfo(offloadInfo) {
916     LOG(DEBUG) << __func__;
917 }
918 
defaultOnClose()919 void StreamOut::defaultOnClose() {
920     mContextInstance.reset();
921 }
922 
updateOffloadMetadata(const AudioOffloadMetadata & in_offloadMetadata)923 ndk::ScopedAStatus StreamOut::updateOffloadMetadata(
924         const AudioOffloadMetadata& in_offloadMetadata) {
925     LOG(DEBUG) << __func__;
926     if (isClosed()) {
927         LOG(ERROR) << __func__ << ": stream was closed";
928         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
929     }
930     if (!mOffloadInfo.has_value()) {
931         LOG(ERROR) << __func__ << ": not a compressed offload stream";
932         return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
933     }
934     if (in_offloadMetadata.sampleRate < 0) {
935         LOG(ERROR) << __func__ << ": invalid sample rate value: " << in_offloadMetadata.sampleRate;
936         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
937     }
938     if (in_offloadMetadata.averageBitRatePerSecond < 0) {
939         LOG(ERROR) << __func__
940                    << ": invalid average BPS value: " << in_offloadMetadata.averageBitRatePerSecond;
941         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
942     }
943     if (in_offloadMetadata.delayFrames < 0) {
944         LOG(ERROR) << __func__
945                    << ": invalid delay frames value: " << in_offloadMetadata.delayFrames;
946         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
947     }
948     if (in_offloadMetadata.paddingFrames < 0) {
949         LOG(ERROR) << __func__
950                    << ": invalid padding frames value: " << in_offloadMetadata.paddingFrames;
951         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
952     }
953     mOffloadMetadata = in_offloadMetadata;
954     return ndk::ScopedAStatus::ok();
955 }
956 
getHwVolume(std::vector<float> * _aidl_return)957 ndk::ScopedAStatus StreamOut::getHwVolume(std::vector<float>* _aidl_return) {
958     LOG(DEBUG) << __func__;
959     (void)_aidl_return;
960     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
961 }
962 
setHwVolume(const std::vector<float> & in_channelVolumes)963 ndk::ScopedAStatus StreamOut::setHwVolume(const std::vector<float>& in_channelVolumes) {
964     LOG(DEBUG) << __func__ << ": gains " << ::android::internal::ToString(in_channelVolumes);
965     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
966 }
967 
getAudioDescriptionMixLevel(float * _aidl_return)968 ndk::ScopedAStatus StreamOut::getAudioDescriptionMixLevel(float* _aidl_return) {
969     LOG(DEBUG) << __func__;
970     (void)_aidl_return;
971     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
972 }
973 
setAudioDescriptionMixLevel(float in_leveldB)974 ndk::ScopedAStatus StreamOut::setAudioDescriptionMixLevel(float in_leveldB) {
975     LOG(DEBUG) << __func__ << ": description mix level " << in_leveldB;
976     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
977 }
978 
getDualMonoMode(AudioDualMonoMode * _aidl_return)979 ndk::ScopedAStatus StreamOut::getDualMonoMode(AudioDualMonoMode* _aidl_return) {
980     LOG(DEBUG) << __func__;
981     (void)_aidl_return;
982     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
983 }
984 
setDualMonoMode(AudioDualMonoMode in_mode)985 ndk::ScopedAStatus StreamOut::setDualMonoMode(AudioDualMonoMode in_mode) {
986     LOG(DEBUG) << __func__ << ": dual mono mode " << toString(in_mode);
987     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
988 }
989 
getRecommendedLatencyModes(std::vector<AudioLatencyMode> * _aidl_return)990 ndk::ScopedAStatus StreamOut::getRecommendedLatencyModes(
991         std::vector<AudioLatencyMode>* _aidl_return) {
992     LOG(DEBUG) << __func__;
993     (void)_aidl_return;
994     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
995 }
996 
setLatencyMode(AudioLatencyMode in_mode)997 ndk::ScopedAStatus StreamOut::setLatencyMode(AudioLatencyMode in_mode) {
998     LOG(DEBUG) << __func__ << ": latency mode " << toString(in_mode);
999     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
1000 }
1001 
getPlaybackRateParameters(AudioPlaybackRate * _aidl_return)1002 ndk::ScopedAStatus StreamOut::getPlaybackRateParameters(AudioPlaybackRate* _aidl_return) {
1003     LOG(DEBUG) << __func__;
1004     (void)_aidl_return;
1005     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
1006 }
1007 
setPlaybackRateParameters(const AudioPlaybackRate & in_playbackRate)1008 ndk::ScopedAStatus StreamOut::setPlaybackRateParameters(const AudioPlaybackRate& in_playbackRate) {
1009     LOG(DEBUG) << __func__ << ": " << in_playbackRate.toString();
1010     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
1011 }
1012 
selectPresentation(int32_t in_presentationId,int32_t in_programId)1013 ndk::ScopedAStatus StreamOut::selectPresentation(int32_t in_presentationId, int32_t in_programId) {
1014     LOG(DEBUG) << __func__ << ": presentationId " << in_presentationId << ", programId "
1015                << in_programId;
1016     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
1017 }
1018 
StreamOutHwVolumeHelper(const StreamContext * context)1019 StreamOutHwVolumeHelper::StreamOutHwVolumeHelper(const StreamContext* context)
1020     : mChannelCount(getChannelCount(context->getChannelLayout())),
1021       mHwVolumes(mChannelCount, 0.0f) {}
1022 
getHwVolumeImpl(std::vector<float> * _aidl_return)1023 ndk::ScopedAStatus StreamOutHwVolumeHelper::getHwVolumeImpl(std::vector<float>* _aidl_return) {
1024     *_aidl_return = mHwVolumes;
1025     LOG(DEBUG) << __func__ << ": returning " << ::android::internal::ToString(*_aidl_return);
1026     return ndk::ScopedAStatus::ok();
1027 }
1028 
setHwVolumeImpl(const std::vector<float> & in_channelVolumes)1029 ndk::ScopedAStatus StreamOutHwVolumeHelper::setHwVolumeImpl(
1030         const std::vector<float>& in_channelVolumes) {
1031     LOG(DEBUG) << __func__ << ": volumes " << ::android::internal::ToString(in_channelVolumes);
1032     if (in_channelVolumes.size() != mChannelCount) {
1033         LOG(ERROR) << __func__
1034                    << ": channel count does not match stream channel count: " << mChannelCount;
1035         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
1036     }
1037     for (float volume : in_channelVolumes) {
1038         if (volume < StreamOut::HW_VOLUME_MIN || volume > StreamOut::HW_VOLUME_MAX) {
1039             LOG(ERROR) << __func__ << ": volume value out of range: " << volume;
1040             return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
1041         }
1042     }
1043     mHwVolumes = in_channelVolumes;
1044     return ndk::ScopedAStatus::ok();
1045 }
1046 
1047 }  // namespace aidl::android::hardware::audio::core
1048