blob: 193c793ba0d31166d7884a7495a4562a3e935a64 [file] [log] [blame]
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001/*
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#define LOG_TAG "AHAL_Stream"
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000018#include <android-base/logging.h>
Mikhail Naganove9f10fc2022-10-14 23:31:52 +000019#include <android/binder_ibinder_platform.h>
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +000020#include <utils/SystemClock.h>
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000021
Mikhail Naganovef6bc742022-10-06 00:14:19 +000022#include <Utils.h>
23
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +000024#include "core-impl/Module.h"
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000025#include "core-impl/Stream.h"
26
Mikhail Naganovd1509422023-02-24 19:50:51 -080027using aidl::android::hardware::audio::common::AudioOffloadMetadata;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000028using aidl::android::hardware::audio::common::SinkMetadata;
29using aidl::android::hardware::audio::common::SourceMetadata;
Mikhail Naganovef6bc742022-10-06 00:14:19 +000030using aidl::android::media::audio::common::AudioDevice;
Mikhail Naganov74927202022-12-19 16:37:14 +000031using aidl::android::media::audio::common::AudioDualMonoMode;
32using aidl::android::media::audio::common::AudioLatencyMode;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000033using aidl::android::media::audio::common::AudioOffloadInfo;
Mikhail Naganov74927202022-12-19 16:37:14 +000034using aidl::android::media::audio::common::AudioPlaybackRate;
Mikhail Naganov6725ef52023-02-09 17:52:50 -080035using aidl::android::media::audio::common::MicrophoneDynamicInfo;
36using aidl::android::media::audio::common::MicrophoneInfo;
Mikhail Naganovef6bc742022-10-06 00:14:19 +000037using android::hardware::audio::common::getChannelCount;
38using android::hardware::audio::common::getFrameSizeInBytes;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000039
40namespace aidl::android::hardware::audio::core {
41
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +000042void StreamContext::fillDescriptor(StreamDescriptor* desc) {
43 if (mCommandMQ) {
44 desc->command = mCommandMQ->dupeDesc();
45 }
46 if (mReplyMQ) {
47 desc->reply = mReplyMQ->dupeDesc();
48 }
49 if (mDataMQ) {
Mikhail Naganovef6bc742022-10-06 00:14:19 +000050 const size_t frameSize = getFrameSize();
51 desc->frameSizeBytes = frameSize;
52 desc->bufferSizeFrames = mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize() / frameSize;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +000053 desc->audio.set<StreamDescriptor::AudioBuffer::Tag::fmq>(mDataMQ->dupeDesc());
54 }
Mikhail Naganov6a4872d2022-06-15 21:39:04 +000055}
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000056
Mikhail Naganovef6bc742022-10-06 00:14:19 +000057size_t StreamContext::getFrameSize() const {
58 return getFrameSizeInBytes(mFormat, mChannelLayout);
59}
60
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +000061bool StreamContext::isValid() const {
62 if (mCommandMQ && !mCommandMQ->isValid()) {
63 LOG(ERROR) << "command FMQ is invalid";
64 return false;
65 }
66 if (mReplyMQ && !mReplyMQ->isValid()) {
67 LOG(ERROR) << "reply FMQ is invalid";
68 return false;
69 }
Mikhail Naganovef6bc742022-10-06 00:14:19 +000070 if (getFrameSize() == 0) {
71 LOG(ERROR) << "frame size is invalid";
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +000072 return false;
73 }
74 if (mDataMQ && !mDataMQ->isValid()) {
75 LOG(ERROR) << "data FMQ is invalid";
76 return false;
77 }
78 return true;
79}
80
81void StreamContext::reset() {
82 mCommandMQ.reset();
83 mReplyMQ.reset();
84 mDataMQ.reset();
85}
86
87std::string StreamWorkerCommonLogic::init() {
88 if (mCommandMQ == nullptr) return "Command MQ is null";
89 if (mReplyMQ == nullptr) return "Reply MQ is null";
90 if (mDataMQ == nullptr) return "Data MQ is null";
Mikhail Naganovf429c032023-01-07 00:24:50 +000091 if (sizeof(DataBufferElement) != mDataMQ->getQuantumSize()) {
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +000092 return "Unexpected Data MQ quantum size: " + std::to_string(mDataMQ->getQuantumSize());
93 }
94 mDataBufferSize = mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize();
Mikhail Naganovf429c032023-01-07 00:24:50 +000095 mDataBuffer.reset(new (std::nothrow) DataBufferElement[mDataBufferSize]);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +000096 if (mDataBuffer == nullptr) {
97 return "Failed to allocate data buffer for element count " +
98 std::to_string(mDataMQ->getQuantumCount()) +
99 ", size in bytes: " + std::to_string(mDataBufferSize);
100 }
Mikhail Naganovf429c032023-01-07 00:24:50 +0000101 if (::android::status_t status = mDriver->init(); status != STATUS_OK) {
102 return "Failed to initialize the driver: " + std::to_string(status);
103 }
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000104 return "";
105}
106
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000107void StreamWorkerCommonLogic::populateReply(StreamDescriptor::Reply* reply,
108 bool isConnected) const {
Mikhail Naganov549a8222022-11-23 18:30:07 +0000109 reply->status = STATUS_OK;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000110 if (isConnected) {
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000111 reply->observable.frames = mFrameCount;
112 reply->observable.timeNs = ::android::elapsedRealtimeNano();
113 } else {
Mikhail Naganov549a8222022-11-23 18:30:07 +0000114 reply->observable.frames = StreamDescriptor::Position::UNKNOWN;
115 reply->observable.timeNs = StreamDescriptor::Position::UNKNOWN;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000116 }
117}
118
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000119void StreamWorkerCommonLogic::populateReplyWrongState(
120 StreamDescriptor::Reply* reply, const StreamDescriptor::Command& command) const {
121 LOG(WARNING) << "command '" << toString(command.getTag())
122 << "' can not be handled in the state " << toString(mState);
123 reply->status = STATUS_INVALID_OPERATION;
124}
125
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000126const std::string StreamInWorkerLogic::kThreadName = "reader";
127
128StreamInWorkerLogic::Status StreamInWorkerLogic::cycle() {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000129 // Note: for input streams, draining is driven by the client, thus
130 // "empty buffer" condition can only happen while handling the 'burst'
131 // command. Thus, unlike for output streams, it does not make sense to
132 // delay the 'DRAINING' state here by 'mTransientStateDelayMs'.
133 // TODO: Add a delay for transitions of async operations when/if they added.
134
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000135 StreamDescriptor::Command command{};
136 if (!mCommandMQ->readBlocking(&command, 1)) {
137 LOG(ERROR) << __func__ << ": reading of command from MQ failed";
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000138 mState = StreamDescriptor::State::ERROR;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000139 return Status::ABORT;
140 }
Mikhail Naganov1f72fd42023-02-02 15:26:04 -0800141 using Tag = StreamDescriptor::Command::Tag;
142 using LogSeverity = ::android::base::LogSeverity;
143 const LogSeverity severity =
144 command.getTag() == Tag::burst || command.getTag() == Tag::getStatus
145 ? LogSeverity::VERBOSE
146 : LogSeverity::DEBUG;
147 LOG(severity) << __func__ << ": received command " << command.toString() << " in "
148 << kThreadName;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000149 StreamDescriptor::Reply reply{};
Mikhail Naganov98334432022-11-09 02:44:32 +0000150 reply.status = STATUS_BAD_VALUE;
Mikhail Naganov98334432022-11-09 02:44:32 +0000151 switch (command.getTag()) {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000152 case Tag::halReservedExit:
153 if (const int32_t cookie = command.get<Tag::halReservedExit>();
Mikhail Naganov98334432022-11-09 02:44:32 +0000154 cookie == mInternalCommandCookie) {
Mikhail Naganov98334432022-11-09 02:44:32 +0000155 setClosed();
156 // This is an internal command, no need to reply.
157 return Status::EXIT;
158 } else {
159 LOG(WARNING) << __func__ << ": EXIT command has a bad cookie: " << cookie;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000160 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000161 break;
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000162 case Tag::getStatus:
163 populateReply(&reply, mIsConnected);
164 break;
Mikhail Naganov98334432022-11-09 02:44:32 +0000165 case Tag::start:
Mikhail Naganov98334432022-11-09 02:44:32 +0000166 if (mState == StreamDescriptor::State::STANDBY ||
167 mState == StreamDescriptor::State::DRAINING) {
168 populateReply(&reply, mIsConnected);
169 mState = mState == StreamDescriptor::State::STANDBY
170 ? StreamDescriptor::State::IDLE
171 : StreamDescriptor::State::ACTIVE;
172 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000173 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000174 }
175 break;
176 case Tag::burst:
177 if (const int32_t fmqByteCount = command.get<Tag::burst>(); fmqByteCount >= 0) {
Mikhail Naganov1f72fd42023-02-02 15:26:04 -0800178 LOG(VERBOSE) << __func__ << ": '" << toString(command.getTag()) << "' command for "
179 << fmqByteCount << " bytes";
Mikhail Naganov98334432022-11-09 02:44:32 +0000180 if (mState == StreamDescriptor::State::IDLE ||
181 mState == StreamDescriptor::State::ACTIVE ||
182 mState == StreamDescriptor::State::PAUSED ||
183 mState == StreamDescriptor::State::DRAINING) {
184 if (!read(fmqByteCount, &reply)) {
185 mState = StreamDescriptor::State::ERROR;
186 }
187 if (mState == StreamDescriptor::State::IDLE ||
188 mState == StreamDescriptor::State::PAUSED) {
189 mState = StreamDescriptor::State::ACTIVE;
190 } else if (mState == StreamDescriptor::State::DRAINING) {
191 // To simplify the reference code, we assume that the read operation
192 // has consumed all the data remaining in the hardware buffer.
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000193 // In a real implementation, here we would either remain in
194 // the 'DRAINING' state, or transfer to 'STANDBY' depending on the
195 // buffer state.
Mikhail Naganov98334432022-11-09 02:44:32 +0000196 mState = StreamDescriptor::State::STANDBY;
197 }
198 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000199 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000200 }
201 } else {
202 LOG(WARNING) << __func__ << ": invalid burst byte count: " << fmqByteCount;
203 }
204 break;
205 case Tag::drain:
Mikhail Naganovf429c032023-01-07 00:24:50 +0000206 if (const auto mode = command.get<Tag::drain>();
207 mode == StreamDescriptor::DrainMode::DRAIN_UNSPECIFIED) {
Mikhail Naganov30301a42022-09-13 01:20:45 +0000208 if (mState == StreamDescriptor::State::ACTIVE) {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000209 if (::android::status_t status = mDriver->drain(mode);
210 status == ::android::OK) {
211 populateReply(&reply, mIsConnected);
212 mState = StreamDescriptor::State::DRAINING;
213 } else {
214 LOG(ERROR) << __func__ << ": drain failed: " << status;
215 mState = StreamDescriptor::State::ERROR;
216 }
Mikhail Naganov30301a42022-09-13 01:20:45 +0000217 } else {
218 populateReplyWrongState(&reply, command);
219 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000220 } else {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000221 LOG(WARNING) << __func__ << ": invalid drain mode: " << toString(mode);
Mikhail Naganov98334432022-11-09 02:44:32 +0000222 }
223 break;
224 case Tag::standby:
Mikhail Naganov98334432022-11-09 02:44:32 +0000225 if (mState == StreamDescriptor::State::IDLE) {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000226 if (::android::status_t status = mDriver->standby(); status == ::android::OK) {
227 populateReply(&reply, mIsConnected);
228 mState = StreamDescriptor::State::STANDBY;
229 } else {
230 LOG(ERROR) << __func__ << ": standby failed: " << status;
231 mState = StreamDescriptor::State::ERROR;
232 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000233 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000234 populateReplyWrongState(&reply, command);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000235 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000236 break;
237 case Tag::pause:
Mikhail Naganov98334432022-11-09 02:44:32 +0000238 if (mState == StreamDescriptor::State::ACTIVE) {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000239 if (::android::status_t status = mDriver->pause(); status == ::android::OK) {
240 populateReply(&reply, mIsConnected);
241 mState = StreamDescriptor::State::PAUSED;
242 } else {
243 LOG(ERROR) << __func__ << ": pause failed: " << status;
244 mState = StreamDescriptor::State::ERROR;
245 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000246 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000247 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000248 }
249 break;
250 case Tag::flush:
Mikhail Naganov98334432022-11-09 02:44:32 +0000251 if (mState == StreamDescriptor::State::PAUSED) {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000252 if (::android::status_t status = mDriver->flush(); status == ::android::OK) {
253 populateReply(&reply, mIsConnected);
254 mState = StreamDescriptor::State::STANDBY;
255 } else {
256 LOG(ERROR) << __func__ << ": flush failed: " << status;
257 mState = StreamDescriptor::State::ERROR;
258 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000259 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000260 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000261 }
262 break;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000263 }
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000264 reply.state = mState;
Mikhail Naganov1f72fd42023-02-02 15:26:04 -0800265 LOG(severity) << __func__ << ": writing reply " << reply.toString();
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000266 if (!mReplyMQ->writeBlocking(&reply, 1)) {
267 LOG(ERROR) << __func__ << ": writing of reply " << reply.toString() << " to MQ failed";
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000268 mState = StreamDescriptor::State::ERROR;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000269 return Status::ABORT;
270 }
271 return Status::CONTINUE;
272}
273
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000274bool StreamInWorkerLogic::read(size_t clientSize, StreamDescriptor::Reply* reply) {
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000275 const size_t byteCount = std::min({clientSize, mDataMQ->availableToWrite(), mDataBufferSize});
276 const bool isConnected = mIsConnected;
Mikhail Naganovf429c032023-01-07 00:24:50 +0000277 size_t actualFrameCount = 0;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000278 bool fatal = false;
Mikhail Naganovf429c032023-01-07 00:24:50 +0000279 int32_t latency = Module::kLatencyMs;
280 if (isConnected) {
281 if (::android::status_t status = mDriver->transfer(
282 mDataBuffer.get(), byteCount / mFrameSize, &actualFrameCount, &latency);
283 status != ::android::OK) {
284 fatal = true;
285 LOG(ERROR) << __func__ << ": read failed: " << status;
286 }
287 } else {
288 usleep(3000); // Simulate blocking transfer delay.
289 for (size_t i = 0; i < byteCount; ++i) mDataBuffer[i] = 0;
290 actualFrameCount = byteCount / mFrameSize;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000291 }
Mikhail Naganovf429c032023-01-07 00:24:50 +0000292 const size_t actualByteCount = actualFrameCount * mFrameSize;
293 if (bool success =
294 actualByteCount > 0 ? mDataMQ->write(&mDataBuffer[0], actualByteCount) : true;
295 success) {
Mikhail Naganov1f72fd42023-02-02 15:26:04 -0800296 LOG(VERBOSE) << __func__ << ": writing of " << actualByteCount << " bytes into data MQ"
297 << " succeeded; connected? " << isConnected;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000298 // Frames are provided and counted regardless of connection status.
Mikhail Naganovf429c032023-01-07 00:24:50 +0000299 reply->fmqByteCount += actualByteCount;
300 mFrameCount += actualFrameCount;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000301 populateReply(reply, isConnected);
302 } else {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000303 LOG(WARNING) << __func__ << ": writing of " << actualByteCount
304 << " bytes of data to MQ failed";
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000305 reply->status = STATUS_NOT_ENOUGH_DATA;
306 }
Mikhail Naganovf429c032023-01-07 00:24:50 +0000307 reply->latencyMs = latency;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000308 return !fatal;
309}
310
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000311const std::string StreamOutWorkerLogic::kThreadName = "writer";
312
313StreamOutWorkerLogic::Status StreamOutWorkerLogic::cycle() {
Mikhail Naganov30301a42022-09-13 01:20:45 +0000314 if (mState == StreamDescriptor::State::DRAINING ||
315 mState == StreamDescriptor::State::TRANSFERRING) {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000316 if (auto stateDurationMs = std::chrono::duration_cast<std::chrono::milliseconds>(
317 std::chrono::steady_clock::now() - mTransientStateStart);
318 stateDurationMs >= mTransientStateDelayMs) {
Mikhail Naganov30301a42022-09-13 01:20:45 +0000319 if (mAsyncCallback == nullptr) {
320 // In blocking mode, mState can only be DRAINING.
321 mState = StreamDescriptor::State::IDLE;
322 } else {
323 // In a real implementation, the driver should notify the HAL about
324 // drain or transfer completion. In the stub, we switch unconditionally.
325 if (mState == StreamDescriptor::State::DRAINING) {
326 mState = StreamDescriptor::State::IDLE;
327 ndk::ScopedAStatus status = mAsyncCallback->onDrainReady();
328 if (!status.isOk()) {
329 LOG(ERROR) << __func__ << ": error from onDrainReady: " << status;
330 }
331 } else {
332 mState = StreamDescriptor::State::ACTIVE;
333 ndk::ScopedAStatus status = mAsyncCallback->onTransferReady();
334 if (!status.isOk()) {
335 LOG(ERROR) << __func__ << ": error from onTransferReady: " << status;
336 }
337 }
338 }
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000339 if (mTransientStateDelayMs.count() != 0) {
340 LOG(DEBUG) << __func__ << ": switched to state " << toString(mState)
341 << " after a timeout";
342 }
343 }
344 }
345
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000346 StreamDescriptor::Command command{};
347 if (!mCommandMQ->readBlocking(&command, 1)) {
348 LOG(ERROR) << __func__ << ": reading of command from MQ failed";
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000349 mState = StreamDescriptor::State::ERROR;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000350 return Status::ABORT;
351 }
Mikhail Naganov1f72fd42023-02-02 15:26:04 -0800352 using Tag = StreamDescriptor::Command::Tag;
353 using LogSeverity = ::android::base::LogSeverity;
354 const LogSeverity severity =
355 command.getTag() == Tag::burst || command.getTag() == Tag::getStatus
356 ? LogSeverity::VERBOSE
357 : LogSeverity::DEBUG;
358 LOG(severity) << __func__ << ": received command " << command.toString() << " in "
359 << kThreadName;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000360 StreamDescriptor::Reply reply{};
Mikhail Naganov98334432022-11-09 02:44:32 +0000361 reply.status = STATUS_BAD_VALUE;
362 using Tag = StreamDescriptor::Command::Tag;
363 switch (command.getTag()) {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000364 case Tag::halReservedExit:
365 if (const int32_t cookie = command.get<Tag::halReservedExit>();
Mikhail Naganov98334432022-11-09 02:44:32 +0000366 cookie == mInternalCommandCookie) {
Mikhail Naganov98334432022-11-09 02:44:32 +0000367 setClosed();
368 // This is an internal command, no need to reply.
369 return Status::EXIT;
370 } else {
371 LOG(WARNING) << __func__ << ": EXIT command has a bad cookie: " << cookie;
372 }
373 break;
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000374 case Tag::getStatus:
375 populateReply(&reply, mIsConnected);
376 break;
Mikhail Naganov30301a42022-09-13 01:20:45 +0000377 case Tag::start: {
378 bool commandAccepted = true;
Mikhail Naganov98334432022-11-09 02:44:32 +0000379 switch (mState) {
380 case StreamDescriptor::State::STANDBY:
381 mState = StreamDescriptor::State::IDLE;
382 break;
383 case StreamDescriptor::State::PAUSED:
384 mState = StreamDescriptor::State::ACTIVE;
385 break;
386 case StreamDescriptor::State::DRAIN_PAUSED:
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000387 switchToTransientState(StreamDescriptor::State::DRAINING);
Mikhail Naganov30301a42022-09-13 01:20:45 +0000388 break;
389 case StreamDescriptor::State::TRANSFER_PAUSED:
390 switchToTransientState(StreamDescriptor::State::TRANSFERRING);
Mikhail Naganov98334432022-11-09 02:44:32 +0000391 break;
392 default:
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000393 populateReplyWrongState(&reply, command);
Mikhail Naganov30301a42022-09-13 01:20:45 +0000394 commandAccepted = false;
Mikhail Naganov98334432022-11-09 02:44:32 +0000395 }
Mikhail Naganov30301a42022-09-13 01:20:45 +0000396 if (commandAccepted) {
397 populateReply(&reply, mIsConnected);
398 }
399 } break;
Mikhail Naganov98334432022-11-09 02:44:32 +0000400 case Tag::burst:
401 if (const int32_t fmqByteCount = command.get<Tag::burst>(); fmqByteCount >= 0) {
Mikhail Naganov1f72fd42023-02-02 15:26:04 -0800402 LOG(VERBOSE) << __func__ << ": '" << toString(command.getTag()) << "' command for "
403 << fmqByteCount << " bytes";
Mikhail Naganov30301a42022-09-13 01:20:45 +0000404 if (mState != StreamDescriptor::State::ERROR &&
405 mState != StreamDescriptor::State::TRANSFERRING &&
406 mState != StreamDescriptor::State::TRANSFER_PAUSED) {
Mikhail Naganov98334432022-11-09 02:44:32 +0000407 if (!write(fmqByteCount, &reply)) {
408 mState = StreamDescriptor::State::ERROR;
409 }
410 if (mState == StreamDescriptor::State::STANDBY ||
Mikhail Naganov30301a42022-09-13 01:20:45 +0000411 mState == StreamDescriptor::State::DRAIN_PAUSED ||
412 mState == StreamDescriptor::State::PAUSED) {
413 if (mAsyncCallback == nullptr ||
414 mState != StreamDescriptor::State::DRAIN_PAUSED) {
415 mState = StreamDescriptor::State::PAUSED;
416 } else {
417 mState = StreamDescriptor::State::TRANSFER_PAUSED;
418 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000419 } else if (mState == StreamDescriptor::State::IDLE ||
Mikhail Naganov30301a42022-09-13 01:20:45 +0000420 mState == StreamDescriptor::State::DRAINING ||
421 mState == StreamDescriptor::State::ACTIVE) {
422 if (mAsyncCallback == nullptr || reply.fmqByteCount == fmqByteCount) {
423 mState = StreamDescriptor::State::ACTIVE;
424 } else {
425 switchToTransientState(StreamDescriptor::State::TRANSFERRING);
426 }
427 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000428 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000429 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000430 }
431 } else {
432 LOG(WARNING) << __func__ << ": invalid burst byte count: " << fmqByteCount;
433 }
434 break;
435 case Tag::drain:
Mikhail Naganovf429c032023-01-07 00:24:50 +0000436 if (const auto mode = command.get<Tag::drain>();
437 mode == StreamDescriptor::DrainMode::DRAIN_ALL ||
438 mode == StreamDescriptor::DrainMode::DRAIN_EARLY_NOTIFY) {
Mikhail Naganov30301a42022-09-13 01:20:45 +0000439 if (mState == StreamDescriptor::State::ACTIVE ||
440 mState == StreamDescriptor::State::TRANSFERRING) {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000441 if (::android::status_t status = mDriver->drain(mode);
442 status == ::android::OK) {
443 populateReply(&reply, mIsConnected);
444 if (mState == StreamDescriptor::State::ACTIVE && mForceSynchronousDrain) {
445 mState = StreamDescriptor::State::IDLE;
446 } else {
447 switchToTransientState(StreamDescriptor::State::DRAINING);
448 }
Mikhail Naganov194daaa2023-01-05 22:34:20 +0000449 } else {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000450 LOG(ERROR) << __func__ << ": drain failed: " << status;
451 mState = StreamDescriptor::State::ERROR;
Mikhail Naganov194daaa2023-01-05 22:34:20 +0000452 }
Mikhail Naganov30301a42022-09-13 01:20:45 +0000453 } else if (mState == StreamDescriptor::State::TRANSFER_PAUSED) {
454 mState = StreamDescriptor::State::DRAIN_PAUSED;
455 populateReply(&reply, mIsConnected);
456 } else {
457 populateReplyWrongState(&reply, command);
458 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000459 } else {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000460 LOG(WARNING) << __func__ << ": invalid drain mode: " << toString(mode);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000461 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000462 break;
463 case Tag::standby:
Mikhail Naganov98334432022-11-09 02:44:32 +0000464 if (mState == StreamDescriptor::State::IDLE) {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000465 if (::android::status_t status = mDriver->standby(); status == ::android::OK) {
466 populateReply(&reply, mIsConnected);
467 mState = StreamDescriptor::State::STANDBY;
468 } else {
469 LOG(ERROR) << __func__ << ": standby failed: " << status;
470 mState = StreamDescriptor::State::ERROR;
471 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000472 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000473 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000474 }
475 break;
Mikhail Naganov30301a42022-09-13 01:20:45 +0000476 case Tag::pause: {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000477 std::optional<StreamDescriptor::State> nextState;
Mikhail Naganov30301a42022-09-13 01:20:45 +0000478 switch (mState) {
479 case StreamDescriptor::State::ACTIVE:
Mikhail Naganovf429c032023-01-07 00:24:50 +0000480 nextState = StreamDescriptor::State::PAUSED;
Mikhail Naganov30301a42022-09-13 01:20:45 +0000481 break;
482 case StreamDescriptor::State::DRAINING:
Mikhail Naganovf429c032023-01-07 00:24:50 +0000483 nextState = StreamDescriptor::State::DRAIN_PAUSED;
Mikhail Naganov30301a42022-09-13 01:20:45 +0000484 break;
485 case StreamDescriptor::State::TRANSFERRING:
Mikhail Naganovf429c032023-01-07 00:24:50 +0000486 nextState = StreamDescriptor::State::TRANSFER_PAUSED;
Mikhail Naganov30301a42022-09-13 01:20:45 +0000487 break;
488 default:
489 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000490 }
Mikhail Naganovf429c032023-01-07 00:24:50 +0000491 if (nextState.has_value()) {
492 if (::android::status_t status = mDriver->pause(); status == ::android::OK) {
493 populateReply(&reply, mIsConnected);
494 mState = nextState.value();
495 } else {
496 LOG(ERROR) << __func__ << ": pause failed: " << status;
497 mState = StreamDescriptor::State::ERROR;
498 }
Mikhail Naganov30301a42022-09-13 01:20:45 +0000499 }
500 } break;
Mikhail Naganov98334432022-11-09 02:44:32 +0000501 case Tag::flush:
Mikhail Naganov98334432022-11-09 02:44:32 +0000502 if (mState == StreamDescriptor::State::PAUSED ||
Mikhail Naganov30301a42022-09-13 01:20:45 +0000503 mState == StreamDescriptor::State::DRAIN_PAUSED ||
504 mState == StreamDescriptor::State::TRANSFER_PAUSED) {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000505 if (::android::status_t status = mDriver->flush(); status == ::android::OK) {
506 populateReply(&reply, mIsConnected);
507 mState = StreamDescriptor::State::IDLE;
508 } else {
509 LOG(ERROR) << __func__ << ": flush failed: " << status;
510 mState = StreamDescriptor::State::ERROR;
511 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000512 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000513 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000514 }
515 break;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000516 }
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000517 reply.state = mState;
Mikhail Naganov1f72fd42023-02-02 15:26:04 -0800518 LOG(severity) << __func__ << ": writing reply " << reply.toString();
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000519 if (!mReplyMQ->writeBlocking(&reply, 1)) {
520 LOG(ERROR) << __func__ << ": writing of reply " << reply.toString() << " to MQ failed";
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000521 mState = StreamDescriptor::State::ERROR;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000522 return Status::ABORT;
523 }
524 return Status::CONTINUE;
525}
526
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000527bool StreamOutWorkerLogic::write(size_t clientSize, StreamDescriptor::Reply* reply) {
528 const size_t readByteCount = mDataMQ->availableToRead();
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000529 bool fatal = false;
Mikhail Naganovf429c032023-01-07 00:24:50 +0000530 int32_t latency = Module::kLatencyMs;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000531 if (bool success = readByteCount > 0 ? mDataMQ->read(&mDataBuffer[0], readByteCount) : true) {
532 const bool isConnected = mIsConnected;
Mikhail Naganov1f72fd42023-02-02 15:26:04 -0800533 LOG(VERBOSE) << __func__ << ": reading of " << readByteCount << " bytes from data MQ"
534 << " succeeded; connected? " << isConnected;
Mikhail Naganov20047bc2023-01-05 20:16:07 +0000535 // Amount of data that the HAL module is going to actually use.
536 size_t byteCount = std::min({clientSize, readByteCount, mDataBufferSize});
537 if (byteCount >= mFrameSize && mForceTransientBurst) {
538 // In order to prevent the state machine from going to ACTIVE state,
539 // simulate partial write.
540 byteCount -= mFrameSize;
541 }
Mikhail Naganovf429c032023-01-07 00:24:50 +0000542 size_t actualFrameCount = 0;
543 if (isConnected) {
544 if (::android::status_t status = mDriver->transfer(
545 mDataBuffer.get(), byteCount / mFrameSize, &actualFrameCount, &latency);
546 status != ::android::OK) {
547 fatal = true;
548 LOG(ERROR) << __func__ << ": write failed: " << status;
549 }
550 } else {
551 if (mAsyncCallback == nullptr) {
552 usleep(3000); // Simulate blocking transfer delay.
553 }
554 actualFrameCount = byteCount / mFrameSize;
555 }
556 const size_t actualByteCount = actualFrameCount * mFrameSize;
Mikhail Naganov20047bc2023-01-05 20:16:07 +0000557 // Frames are consumed and counted regardless of the connection status.
Mikhail Naganovf429c032023-01-07 00:24:50 +0000558 reply->fmqByteCount += actualByteCount;
559 mFrameCount += actualFrameCount;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000560 populateReply(reply, isConnected);
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000561 } else {
562 LOG(WARNING) << __func__ << ": reading of " << readByteCount
563 << " bytes of data from MQ failed";
564 reply->status = STATUS_NOT_ENOUGH_DATA;
565 }
Mikhail Naganovf429c032023-01-07 00:24:50 +0000566 reply->latencyMs = latency;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000567 return !fatal;
568}
569
Mikhail Naganovf429c032023-01-07 00:24:50 +0000570template <class Metadata>
571StreamCommonImpl<Metadata>::~StreamCommonImpl() {
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000572 if (!isClosed()) {
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000573 LOG(ERROR) << __func__ << ": stream was not closed prior to destruction, resource leak";
574 stopWorker();
575 // The worker and the context should clean up by themselves via destructors.
576 }
577}
578
Mikhail Naganovf429c032023-01-07 00:24:50 +0000579template <class Metadata>
580void StreamCommonImpl<Metadata>::createStreamCommon(
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000581 const std::shared_ptr<StreamCommonInterface>& delegate) {
582 if (mCommon != nullptr) {
583 LOG(FATAL) << __func__ << ": attempting to create the common interface twice";
584 }
585 mCommon = ndk::SharedRefBase::make<StreamCommon>(delegate);
586 mCommonBinder = mCommon->asBinder();
587 AIBinder_setMinSchedulerPolicy(mCommonBinder.get(), SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
588}
589
Mikhail Naganovf429c032023-01-07 00:24:50 +0000590template <class Metadata>
591ndk::ScopedAStatus StreamCommonImpl<Metadata>::getStreamCommon(
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000592 std::shared_ptr<IStreamCommon>* _aidl_return) {
593 if (mCommon == nullptr) {
594 LOG(FATAL) << __func__ << ": the common interface was not created";
595 }
596 *_aidl_return = mCommon;
597 LOG(DEBUG) << __func__ << ": returning " << _aidl_return->get()->asBinder().get();
598 return ndk::ScopedAStatus::ok();
599}
600
Mikhail Naganovf429c032023-01-07 00:24:50 +0000601template <class Metadata>
602ndk::ScopedAStatus StreamCommonImpl<Metadata>::updateHwAvSyncId(int32_t in_hwAvSyncId) {
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000603 LOG(DEBUG) << __func__ << ": id " << in_hwAvSyncId;
604 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
605}
606
Mikhail Naganovf429c032023-01-07 00:24:50 +0000607template <class Metadata>
608ndk::ScopedAStatus StreamCommonImpl<Metadata>::getVendorParameters(
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000609 const std::vector<std::string>& in_ids, std::vector<VendorParameter>* _aidl_return) {
610 LOG(DEBUG) << __func__ << ": id count: " << in_ids.size();
611 (void)_aidl_return;
612 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
613}
614
Mikhail Naganovf429c032023-01-07 00:24:50 +0000615template <class Metadata>
616ndk::ScopedAStatus StreamCommonImpl<Metadata>::setVendorParameters(
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000617 const std::vector<VendorParameter>& in_parameters, bool in_async) {
618 LOG(DEBUG) << __func__ << ": parameters count " << in_parameters.size()
619 << ", async: " << in_async;
620 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
621}
622
Mikhail Naganovf429c032023-01-07 00:24:50 +0000623template <class Metadata>
624ndk::ScopedAStatus StreamCommonImpl<Metadata>::addEffect(
Mikhail Naganovfb1acde2022-12-12 18:57:36 +0000625 const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect) {
626 if (in_effect == nullptr) {
627 LOG(DEBUG) << __func__ << ": null effect";
628 } else {
629 LOG(DEBUG) << __func__ << ": effect Binder" << in_effect->asBinder().get();
630 }
631 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
632}
633
Mikhail Naganovf429c032023-01-07 00:24:50 +0000634template <class Metadata>
635ndk::ScopedAStatus StreamCommonImpl<Metadata>::removeEffect(
Mikhail Naganovfb1acde2022-12-12 18:57:36 +0000636 const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect) {
637 if (in_effect == nullptr) {
638 LOG(DEBUG) << __func__ << ": null effect";
639 } else {
640 LOG(DEBUG) << __func__ << ": effect Binder" << in_effect->asBinder().get();
641 }
642 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
643}
644
Mikhail Naganovf429c032023-01-07 00:24:50 +0000645template <class Metadata>
646ndk::ScopedAStatus StreamCommonImpl<Metadata>::close() {
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000647 LOG(DEBUG) << __func__;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000648 if (!isClosed()) {
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000649 stopWorker();
650 LOG(DEBUG) << __func__ << ": joining the worker thread...";
Mikhail Naganovf429c032023-01-07 00:24:50 +0000651 mWorker->stop();
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000652 LOG(DEBUG) << __func__ << ": worker thread joined";
653 mContext.reset();
Mikhail Naganovf429c032023-01-07 00:24:50 +0000654 mWorker->setClosed();
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000655 return ndk::ScopedAStatus::ok();
656 } else {
657 LOG(ERROR) << __func__ << ": stream was already closed";
658 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
659 }
660}
661
Mikhail Naganovf429c032023-01-07 00:24:50 +0000662template <class Metadata>
663void StreamCommonImpl<Metadata>::stopWorker() {
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000664 if (auto commandMQ = mContext.getCommandMQ(); commandMQ != nullptr) {
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000665 LOG(DEBUG) << __func__ << ": asking the worker to exit...";
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000666 auto cmd = StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::halReservedExit>(
667 mContext.getInternalCommandCookie());
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000668 // Note: never call 'pause' and 'resume' methods of StreamWorker
669 // in the HAL implementation. These methods are to be used by
670 // the client side only. Preventing the worker loop from running
671 // on the HAL side can cause a deadlock.
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000672 if (!commandMQ->writeBlocking(&cmd, 1)) {
673 LOG(ERROR) << __func__ << ": failed to write exit command to the MQ";
674 }
675 LOG(DEBUG) << __func__ << ": done";
676 }
677}
678
Mikhail Naganovf429c032023-01-07 00:24:50 +0000679template <class Metadata>
680ndk::ScopedAStatus StreamCommonImpl<Metadata>::updateMetadata(const Metadata& metadata) {
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000681 LOG(DEBUG) << __func__;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000682 if (!isClosed()) {
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000683 mMetadata = metadata;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000684 return ndk::ScopedAStatus::ok();
685 }
686 LOG(ERROR) << __func__ << ": stream was closed";
687 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
688}
689
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000690// static
Mikhail Naganovf429c032023-01-07 00:24:50 +0000691ndk::ScopedAStatus StreamIn::initInstance(const std::shared_ptr<StreamIn>& stream) {
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000692 if (auto status = stream->init(); !status.isOk()) {
693 return status;
694 }
695 stream->createStreamCommon(stream);
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000696 return ndk::ScopedAStatus::ok();
697}
698
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000699namespace {
700static std::map<AudioDevice, std::string> transformMicrophones(
701 const std::vector<MicrophoneInfo>& microphones) {
702 std::map<AudioDevice, std::string> result;
703 std::transform(microphones.begin(), microphones.end(), std::inserter(result, result.begin()),
704 [](const auto& mic) { return std::make_pair(mic.device, mic.id); });
705 return result;
706}
707} // namespace
708
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000709StreamIn::StreamIn(const SinkMetadata& sinkMetadata, StreamContext&& context,
Mikhail Naganovf429c032023-01-07 00:24:50 +0000710 const DriverInterface::CreateInstance& createDriver,
711 const StreamWorkerInterface::CreateInstance& createWorker,
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000712 const std::vector<MicrophoneInfo>& microphones)
Mikhail Naganovf429c032023-01-07 00:24:50 +0000713 : StreamCommonImpl<SinkMetadata>(sinkMetadata, std::move(context), createDriver, createWorker),
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000714 mMicrophones(transformMicrophones(microphones)) {
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000715 LOG(DEBUG) << __func__;
716}
717
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000718ndk::ScopedAStatus StreamIn::getActiveMicrophones(
719 std::vector<MicrophoneDynamicInfo>* _aidl_return) {
720 std::vector<MicrophoneDynamicInfo> result;
721 std::vector<MicrophoneDynamicInfo::ChannelMapping> channelMapping{
722 getChannelCount(mContext.getChannelLayout()),
723 MicrophoneDynamicInfo::ChannelMapping::DIRECT};
724 for (auto it = mConnectedDevices.begin(); it != mConnectedDevices.end(); ++it) {
725 if (auto micIt = mMicrophones.find(*it); micIt != mMicrophones.end()) {
726 MicrophoneDynamicInfo dynMic;
727 dynMic.id = micIt->second;
728 dynMic.channelMapping = channelMapping;
729 result.push_back(std::move(dynMic));
730 }
731 }
732 *_aidl_return = std::move(result);
733 LOG(DEBUG) << __func__ << ": returning " << ::android::internal::ToString(*_aidl_return);
734 return ndk::ScopedAStatus::ok();
735}
736
737ndk::ScopedAStatus StreamIn::getMicrophoneDirection(MicrophoneDirection* _aidl_return) {
738 LOG(DEBUG) << __func__;
739 (void)_aidl_return;
740 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
741}
742
743ndk::ScopedAStatus StreamIn::setMicrophoneDirection(MicrophoneDirection in_direction) {
744 LOG(DEBUG) << __func__ << ": direction " << toString(in_direction);
745 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
746}
747
748ndk::ScopedAStatus StreamIn::getMicrophoneFieldDimension(float* _aidl_return) {
749 LOG(DEBUG) << __func__;
750 (void)_aidl_return;
751 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
752}
753
754ndk::ScopedAStatus StreamIn::setMicrophoneFieldDimension(float in_zoom) {
755 LOG(DEBUG) << __func__ << ": zoom " << in_zoom;
756 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
757}
758
Mikhail Naganov383cd422022-10-15 00:25:45 +0000759ndk::ScopedAStatus StreamIn::getHwGain(std::vector<float>* _aidl_return) {
760 LOG(DEBUG) << __func__;
761 (void)_aidl_return;
762 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
763}
764
765ndk::ScopedAStatus StreamIn::setHwGain(const std::vector<float>& in_channelGains) {
766 LOG(DEBUG) << __func__ << ": gains " << ::android::internal::ToString(in_channelGains);
767 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
768}
769
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000770// static
Mikhail Naganovf429c032023-01-07 00:24:50 +0000771ndk::ScopedAStatus StreamOut::initInstance(const std::shared_ptr<StreamOut>& stream) {
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000772 if (auto status = stream->init(); !status.isOk()) {
773 return status;
774 }
775 stream->createStreamCommon(stream);
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000776 return ndk::ScopedAStatus::ok();
777}
778
779StreamOut::StreamOut(const SourceMetadata& sourceMetadata, StreamContext&& context,
Mikhail Naganovf429c032023-01-07 00:24:50 +0000780 const DriverInterface::CreateInstance& createDriver,
781 const StreamWorkerInterface::CreateInstance& createWorker,
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000782 const std::optional<AudioOffloadInfo>& offloadInfo)
Mikhail Naganovf429c032023-01-07 00:24:50 +0000783 : StreamCommonImpl<SourceMetadata>(sourceMetadata, std::move(context), createDriver,
784 createWorker),
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000785 mOffloadInfo(offloadInfo) {
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000786 LOG(DEBUG) << __func__;
787}
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000788
Mikhail Naganovd1509422023-02-24 19:50:51 -0800789ndk::ScopedAStatus StreamOut::updateOffloadMetadata(
790 const AudioOffloadMetadata& in_offloadMetadata) {
791 LOG(DEBUG) << __func__;
792 if (isClosed()) {
793 LOG(ERROR) << __func__ << ": stream was closed";
794 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
795 }
796 if (!mOffloadInfo.has_value()) {
797 LOG(ERROR) << __func__ << ": not a compressed offload stream";
798 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
799 }
800 if (in_offloadMetadata.sampleRate < 0) {
801 LOG(ERROR) << __func__ << ": invalid sample rate value: " << in_offloadMetadata.sampleRate;
802 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
803 }
804 if (in_offloadMetadata.averageBitRatePerSecond < 0) {
805 LOG(ERROR) << __func__
806 << ": invalid average BPS value: " << in_offloadMetadata.averageBitRatePerSecond;
807 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
808 }
809 if (in_offloadMetadata.delayFrames < 0) {
810 LOG(ERROR) << __func__
811 << ": invalid delay frames value: " << in_offloadMetadata.delayFrames;
812 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
813 }
814 if (in_offloadMetadata.paddingFrames < 0) {
815 LOG(ERROR) << __func__
816 << ": invalid padding frames value: " << in_offloadMetadata.paddingFrames;
817 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
818 }
819 mOffloadMetadata = in_offloadMetadata;
820 return ndk::ScopedAStatus::ok();
821}
822
Mikhail Naganov383cd422022-10-15 00:25:45 +0000823ndk::ScopedAStatus StreamOut::getHwVolume(std::vector<float>* _aidl_return) {
824 LOG(DEBUG) << __func__;
825 (void)_aidl_return;
826 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
827}
828
829ndk::ScopedAStatus StreamOut::setHwVolume(const std::vector<float>& in_channelVolumes) {
830 LOG(DEBUG) << __func__ << ": gains " << ::android::internal::ToString(in_channelVolumes);
831 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
832}
833
Mikhail Naganov74927202022-12-19 16:37:14 +0000834ndk::ScopedAStatus StreamOut::getAudioDescriptionMixLevel(float* _aidl_return) {
835 LOG(DEBUG) << __func__;
836 (void)_aidl_return;
837 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
838}
839
840ndk::ScopedAStatus StreamOut::setAudioDescriptionMixLevel(float in_leveldB) {
841 LOG(DEBUG) << __func__ << ": description mix level " << in_leveldB;
842 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
843}
844
845ndk::ScopedAStatus StreamOut::getDualMonoMode(AudioDualMonoMode* _aidl_return) {
846 LOG(DEBUG) << __func__;
847 (void)_aidl_return;
848 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
849}
850
851ndk::ScopedAStatus StreamOut::setDualMonoMode(AudioDualMonoMode in_mode) {
852 LOG(DEBUG) << __func__ << ": dual mono mode " << toString(in_mode);
853 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
854}
855
856ndk::ScopedAStatus StreamOut::getRecommendedLatencyModes(
857 std::vector<AudioLatencyMode>* _aidl_return) {
858 LOG(DEBUG) << __func__;
859 (void)_aidl_return;
860 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
861}
862
863ndk::ScopedAStatus StreamOut::setLatencyMode(AudioLatencyMode in_mode) {
864 LOG(DEBUG) << __func__ << ": latency mode " << toString(in_mode);
865 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
866}
867
868ndk::ScopedAStatus StreamOut::getPlaybackRateParameters(AudioPlaybackRate* _aidl_return) {
869 LOG(DEBUG) << __func__;
870 (void)_aidl_return;
871 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
872}
873
874ndk::ScopedAStatus StreamOut::setPlaybackRateParameters(const AudioPlaybackRate& in_playbackRate) {
875 LOG(DEBUG) << __func__ << ": " << in_playbackRate.toString();
876 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
877}
878
879ndk::ScopedAStatus StreamOut::selectPresentation(int32_t in_presentationId, int32_t in_programId) {
880 LOG(DEBUG) << __func__ << ": presentationId " << in_presentationId << ", programId "
881 << in_programId;
882 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
883}
884
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000885} // namespace aidl::android::hardware::audio::core