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