blob: 49ad2f2d742705c8df9264abc63e0717ab4b444e [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
27using aidl::android::hardware::audio::common::SinkMetadata;
28using aidl::android::hardware::audio::common::SourceMetadata;
Mikhail Naganovef6bc742022-10-06 00:14:19 +000029using aidl::android::media::audio::common::AudioDevice;
Mikhail Naganov74927202022-12-19 16:37:14 +000030using aidl::android::media::audio::common::AudioDualMonoMode;
31using aidl::android::media::audio::common::AudioLatencyMode;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000032using aidl::android::media::audio::common::AudioOffloadInfo;
Mikhail Naganov74927202022-12-19 16:37:14 +000033using aidl::android::media::audio::common::AudioPlaybackRate;
Mikhail Naganov6725ef52023-02-09 17:52:50 -080034using aidl::android::media::audio::common::MicrophoneDynamicInfo;
35using aidl::android::media::audio::common::MicrophoneInfo;
Mikhail Naganovef6bc742022-10-06 00:14:19 +000036using android::hardware::audio::common::getChannelCount;
37using android::hardware::audio::common::getFrameSizeInBytes;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000038
39namespace aidl::android::hardware::audio::core {
40
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +000041void StreamContext::fillDescriptor(StreamDescriptor* desc) {
42 if (mCommandMQ) {
43 desc->command = mCommandMQ->dupeDesc();
44 }
45 if (mReplyMQ) {
46 desc->reply = mReplyMQ->dupeDesc();
47 }
48 if (mDataMQ) {
Mikhail Naganovef6bc742022-10-06 00:14:19 +000049 const size_t frameSize = getFrameSize();
50 desc->frameSizeBytes = frameSize;
51 desc->bufferSizeFrames = mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize() / frameSize;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +000052 desc->audio.set<StreamDescriptor::AudioBuffer::Tag::fmq>(mDataMQ->dupeDesc());
53 }
Mikhail Naganov6a4872d2022-06-15 21:39:04 +000054}
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000055
Mikhail Naganovef6bc742022-10-06 00:14:19 +000056size_t StreamContext::getFrameSize() const {
57 return getFrameSizeInBytes(mFormat, mChannelLayout);
58}
59
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +000060bool StreamContext::isValid() const {
61 if (mCommandMQ && !mCommandMQ->isValid()) {
62 LOG(ERROR) << "command FMQ is invalid";
63 return false;
64 }
65 if (mReplyMQ && !mReplyMQ->isValid()) {
66 LOG(ERROR) << "reply FMQ is invalid";
67 return false;
68 }
Mikhail Naganovef6bc742022-10-06 00:14:19 +000069 if (getFrameSize() == 0) {
70 LOG(ERROR) << "frame size is invalid";
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +000071 return false;
72 }
73 if (mDataMQ && !mDataMQ->isValid()) {
74 LOG(ERROR) << "data FMQ is invalid";
75 return false;
76 }
77 return true;
78}
79
80void StreamContext::reset() {
81 mCommandMQ.reset();
82 mReplyMQ.reset();
83 mDataMQ.reset();
84}
85
86std::string StreamWorkerCommonLogic::init() {
87 if (mCommandMQ == nullptr) return "Command MQ is null";
88 if (mReplyMQ == nullptr) return "Reply MQ is null";
89 if (mDataMQ == nullptr) return "Data MQ is null";
Mikhail Naganovf429c032023-01-07 00:24:50 +000090 if (sizeof(DataBufferElement) != mDataMQ->getQuantumSize()) {
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +000091 return "Unexpected Data MQ quantum size: " + std::to_string(mDataMQ->getQuantumSize());
92 }
93 mDataBufferSize = mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize();
Mikhail Naganovf429c032023-01-07 00:24:50 +000094 mDataBuffer.reset(new (std::nothrow) DataBufferElement[mDataBufferSize]);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +000095 if (mDataBuffer == nullptr) {
96 return "Failed to allocate data buffer for element count " +
97 std::to_string(mDataMQ->getQuantumCount()) +
98 ", size in bytes: " + std::to_string(mDataBufferSize);
99 }
Mikhail Naganovf429c032023-01-07 00:24:50 +0000100 if (::android::status_t status = mDriver->init(); status != STATUS_OK) {
101 return "Failed to initialize the driver: " + std::to_string(status);
102 }
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000103 return "";
104}
105
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000106void StreamWorkerCommonLogic::populateReply(StreamDescriptor::Reply* reply,
107 bool isConnected) const {
Mikhail Naganov549a8222022-11-23 18:30:07 +0000108 reply->status = STATUS_OK;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000109 if (isConnected) {
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000110 reply->observable.frames = mFrameCount;
111 reply->observable.timeNs = ::android::elapsedRealtimeNano();
112 } else {
Mikhail Naganov549a8222022-11-23 18:30:07 +0000113 reply->observable.frames = StreamDescriptor::Position::UNKNOWN;
114 reply->observable.timeNs = StreamDescriptor::Position::UNKNOWN;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000115 }
116}
117
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000118void StreamWorkerCommonLogic::populateReplyWrongState(
119 StreamDescriptor::Reply* reply, const StreamDescriptor::Command& command) const {
120 LOG(WARNING) << "command '" << toString(command.getTag())
121 << "' can not be handled in the state " << toString(mState);
122 reply->status = STATUS_INVALID_OPERATION;
123}
124
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000125const std::string StreamInWorkerLogic::kThreadName = "reader";
126
127StreamInWorkerLogic::Status StreamInWorkerLogic::cycle() {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000128 // Note: for input streams, draining is driven by the client, thus
129 // "empty buffer" condition can only happen while handling the 'burst'
130 // command. Thus, unlike for output streams, it does not make sense to
131 // delay the 'DRAINING' state here by 'mTransientStateDelayMs'.
132 // TODO: Add a delay for transitions of async operations when/if they added.
133
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000134 StreamDescriptor::Command command{};
135 if (!mCommandMQ->readBlocking(&command, 1)) {
136 LOG(ERROR) << __func__ << ": reading of command from MQ failed";
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000137 mState = StreamDescriptor::State::ERROR;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000138 return Status::ABORT;
139 }
Mikhail Naganov1f72fd42023-02-02 15:26:04 -0800140 using Tag = StreamDescriptor::Command::Tag;
141 using LogSeverity = ::android::base::LogSeverity;
142 const LogSeverity severity =
143 command.getTag() == Tag::burst || command.getTag() == Tag::getStatus
144 ? LogSeverity::VERBOSE
145 : LogSeverity::DEBUG;
146 LOG(severity) << __func__ << ": received command " << command.toString() << " in "
147 << kThreadName;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000148 StreamDescriptor::Reply reply{};
Mikhail Naganov98334432022-11-09 02:44:32 +0000149 reply.status = STATUS_BAD_VALUE;
Mikhail Naganov98334432022-11-09 02:44:32 +0000150 switch (command.getTag()) {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000151 case Tag::halReservedExit:
152 if (const int32_t cookie = command.get<Tag::halReservedExit>();
Mikhail Naganov98334432022-11-09 02:44:32 +0000153 cookie == mInternalCommandCookie) {
Mikhail Naganov98334432022-11-09 02:44:32 +0000154 setClosed();
155 // This is an internal command, no need to reply.
156 return Status::EXIT;
157 } else {
158 LOG(WARNING) << __func__ << ": EXIT command has a bad cookie: " << cookie;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000159 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000160 break;
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000161 case Tag::getStatus:
162 populateReply(&reply, mIsConnected);
163 break;
Mikhail Naganov98334432022-11-09 02:44:32 +0000164 case Tag::start:
Mikhail Naganov98334432022-11-09 02:44:32 +0000165 if (mState == StreamDescriptor::State::STANDBY ||
166 mState == StreamDescriptor::State::DRAINING) {
167 populateReply(&reply, mIsConnected);
168 mState = mState == StreamDescriptor::State::STANDBY
169 ? StreamDescriptor::State::IDLE
170 : StreamDescriptor::State::ACTIVE;
171 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000172 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000173 }
174 break;
175 case Tag::burst:
176 if (const int32_t fmqByteCount = command.get<Tag::burst>(); fmqByteCount >= 0) {
Mikhail Naganov1f72fd42023-02-02 15:26:04 -0800177 LOG(VERBOSE) << __func__ << ": '" << toString(command.getTag()) << "' command for "
178 << fmqByteCount << " bytes";
Mikhail Naganov98334432022-11-09 02:44:32 +0000179 if (mState == StreamDescriptor::State::IDLE ||
180 mState == StreamDescriptor::State::ACTIVE ||
181 mState == StreamDescriptor::State::PAUSED ||
182 mState == StreamDescriptor::State::DRAINING) {
183 if (!read(fmqByteCount, &reply)) {
184 mState = StreamDescriptor::State::ERROR;
185 }
186 if (mState == StreamDescriptor::State::IDLE ||
187 mState == StreamDescriptor::State::PAUSED) {
188 mState = StreamDescriptor::State::ACTIVE;
189 } else if (mState == StreamDescriptor::State::DRAINING) {
190 // To simplify the reference code, we assume that the read operation
191 // has consumed all the data remaining in the hardware buffer.
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000192 // In a real implementation, here we would either remain in
193 // the 'DRAINING' state, or transfer to 'STANDBY' depending on the
194 // buffer state.
Mikhail Naganov98334432022-11-09 02:44:32 +0000195 mState = StreamDescriptor::State::STANDBY;
196 }
197 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000198 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000199 }
200 } else {
201 LOG(WARNING) << __func__ << ": invalid burst byte count: " << fmqByteCount;
202 }
203 break;
204 case Tag::drain:
Mikhail Naganovf429c032023-01-07 00:24:50 +0000205 if (const auto mode = command.get<Tag::drain>();
206 mode == StreamDescriptor::DrainMode::DRAIN_UNSPECIFIED) {
Mikhail Naganov30301a42022-09-13 01:20:45 +0000207 if (mState == StreamDescriptor::State::ACTIVE) {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000208 if (::android::status_t status = mDriver->drain(mode);
209 status == ::android::OK) {
210 populateReply(&reply, mIsConnected);
211 mState = StreamDescriptor::State::DRAINING;
212 } else {
213 LOG(ERROR) << __func__ << ": drain failed: " << status;
214 mState = StreamDescriptor::State::ERROR;
215 }
Mikhail Naganov30301a42022-09-13 01:20:45 +0000216 } else {
217 populateReplyWrongState(&reply, command);
218 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000219 } else {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000220 LOG(WARNING) << __func__ << ": invalid drain mode: " << toString(mode);
Mikhail Naganov98334432022-11-09 02:44:32 +0000221 }
222 break;
223 case Tag::standby:
Mikhail Naganov98334432022-11-09 02:44:32 +0000224 if (mState == StreamDescriptor::State::IDLE) {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000225 if (::android::status_t status = mDriver->standby(); status == ::android::OK) {
226 populateReply(&reply, mIsConnected);
227 mState = StreamDescriptor::State::STANDBY;
228 } else {
229 LOG(ERROR) << __func__ << ": standby failed: " << status;
230 mState = StreamDescriptor::State::ERROR;
231 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000232 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000233 populateReplyWrongState(&reply, command);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000234 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000235 break;
236 case Tag::pause:
Mikhail Naganov98334432022-11-09 02:44:32 +0000237 if (mState == StreamDescriptor::State::ACTIVE) {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000238 if (::android::status_t status = mDriver->pause(); status == ::android::OK) {
239 populateReply(&reply, mIsConnected);
240 mState = StreamDescriptor::State::PAUSED;
241 } else {
242 LOG(ERROR) << __func__ << ": pause failed: " << status;
243 mState = StreamDescriptor::State::ERROR;
244 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000245 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000246 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000247 }
248 break;
249 case Tag::flush:
Mikhail Naganov98334432022-11-09 02:44:32 +0000250 if (mState == StreamDescriptor::State::PAUSED) {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000251 if (::android::status_t status = mDriver->flush(); status == ::android::OK) {
252 populateReply(&reply, mIsConnected);
253 mState = StreamDescriptor::State::STANDBY;
254 } else {
255 LOG(ERROR) << __func__ << ": flush failed: " << status;
256 mState = StreamDescriptor::State::ERROR;
257 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000258 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000259 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000260 }
261 break;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000262 }
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000263 reply.state = mState;
Mikhail Naganov1f72fd42023-02-02 15:26:04 -0800264 LOG(severity) << __func__ << ": writing reply " << reply.toString();
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000265 if (!mReplyMQ->writeBlocking(&reply, 1)) {
266 LOG(ERROR) << __func__ << ": writing of reply " << reply.toString() << " to MQ failed";
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000267 mState = StreamDescriptor::State::ERROR;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000268 return Status::ABORT;
269 }
270 return Status::CONTINUE;
271}
272
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000273bool StreamInWorkerLogic::read(size_t clientSize, StreamDescriptor::Reply* reply) {
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000274 const size_t byteCount = std::min({clientSize, mDataMQ->availableToWrite(), mDataBufferSize});
275 const bool isConnected = mIsConnected;
Mikhail Naganovf429c032023-01-07 00:24:50 +0000276 size_t actualFrameCount = 0;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000277 bool fatal = false;
Mikhail Naganovf429c032023-01-07 00:24:50 +0000278 int32_t latency = Module::kLatencyMs;
279 if (isConnected) {
280 if (::android::status_t status = mDriver->transfer(
281 mDataBuffer.get(), byteCount / mFrameSize, &actualFrameCount, &latency);
282 status != ::android::OK) {
283 fatal = true;
284 LOG(ERROR) << __func__ << ": read failed: " << status;
285 }
286 } else {
287 usleep(3000); // Simulate blocking transfer delay.
288 for (size_t i = 0; i < byteCount; ++i) mDataBuffer[i] = 0;
289 actualFrameCount = byteCount / mFrameSize;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000290 }
Mikhail Naganovf429c032023-01-07 00:24:50 +0000291 const size_t actualByteCount = actualFrameCount * mFrameSize;
292 if (bool success =
293 actualByteCount > 0 ? mDataMQ->write(&mDataBuffer[0], actualByteCount) : true;
294 success) {
Mikhail Naganov1f72fd42023-02-02 15:26:04 -0800295 LOG(VERBOSE) << __func__ << ": writing of " << actualByteCount << " bytes into data MQ"
296 << " succeeded; connected? " << isConnected;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000297 // Frames are provided and counted regardless of connection status.
Mikhail Naganovf429c032023-01-07 00:24:50 +0000298 reply->fmqByteCount += actualByteCount;
299 mFrameCount += actualFrameCount;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000300 populateReply(reply, isConnected);
301 } else {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000302 LOG(WARNING) << __func__ << ": writing of " << actualByteCount
303 << " bytes of data to MQ failed";
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000304 reply->status = STATUS_NOT_ENOUGH_DATA;
305 }
Mikhail Naganovf429c032023-01-07 00:24:50 +0000306 reply->latencyMs = latency;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000307 return !fatal;
308}
309
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000310const std::string StreamOutWorkerLogic::kThreadName = "writer";
311
312StreamOutWorkerLogic::Status StreamOutWorkerLogic::cycle() {
Mikhail Naganov30301a42022-09-13 01:20:45 +0000313 if (mState == StreamDescriptor::State::DRAINING ||
314 mState == StreamDescriptor::State::TRANSFERRING) {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000315 if (auto stateDurationMs = std::chrono::duration_cast<std::chrono::milliseconds>(
316 std::chrono::steady_clock::now() - mTransientStateStart);
317 stateDurationMs >= mTransientStateDelayMs) {
Mikhail Naganov30301a42022-09-13 01:20:45 +0000318 if (mAsyncCallback == nullptr) {
319 // In blocking mode, mState can only be DRAINING.
320 mState = StreamDescriptor::State::IDLE;
321 } else {
322 // In a real implementation, the driver should notify the HAL about
323 // drain or transfer completion. In the stub, we switch unconditionally.
324 if (mState == StreamDescriptor::State::DRAINING) {
325 mState = StreamDescriptor::State::IDLE;
326 ndk::ScopedAStatus status = mAsyncCallback->onDrainReady();
327 if (!status.isOk()) {
328 LOG(ERROR) << __func__ << ": error from onDrainReady: " << status;
329 }
330 } else {
331 mState = StreamDescriptor::State::ACTIVE;
332 ndk::ScopedAStatus status = mAsyncCallback->onTransferReady();
333 if (!status.isOk()) {
334 LOG(ERROR) << __func__ << ": error from onTransferReady: " << status;
335 }
336 }
337 }
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000338 if (mTransientStateDelayMs.count() != 0) {
339 LOG(DEBUG) << __func__ << ": switched to state " << toString(mState)
340 << " after a timeout";
341 }
342 }
343 }
344
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000345 StreamDescriptor::Command command{};
346 if (!mCommandMQ->readBlocking(&command, 1)) {
347 LOG(ERROR) << __func__ << ": reading of command from MQ failed";
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000348 mState = StreamDescriptor::State::ERROR;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000349 return Status::ABORT;
350 }
Mikhail Naganov1f72fd42023-02-02 15:26:04 -0800351 using Tag = StreamDescriptor::Command::Tag;
352 using LogSeverity = ::android::base::LogSeverity;
353 const LogSeverity severity =
354 command.getTag() == Tag::burst || command.getTag() == Tag::getStatus
355 ? LogSeverity::VERBOSE
356 : LogSeverity::DEBUG;
357 LOG(severity) << __func__ << ": received command " << command.toString() << " in "
358 << kThreadName;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000359 StreamDescriptor::Reply reply{};
Mikhail Naganov98334432022-11-09 02:44:32 +0000360 reply.status = STATUS_BAD_VALUE;
361 using Tag = StreamDescriptor::Command::Tag;
362 switch (command.getTag()) {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000363 case Tag::halReservedExit:
364 if (const int32_t cookie = command.get<Tag::halReservedExit>();
Mikhail Naganov98334432022-11-09 02:44:32 +0000365 cookie == mInternalCommandCookie) {
Mikhail Naganov98334432022-11-09 02:44:32 +0000366 setClosed();
367 // This is an internal command, no need to reply.
368 return Status::EXIT;
369 } else {
370 LOG(WARNING) << __func__ << ": EXIT command has a bad cookie: " << cookie;
371 }
372 break;
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000373 case Tag::getStatus:
374 populateReply(&reply, mIsConnected);
375 break;
Mikhail Naganov30301a42022-09-13 01:20:45 +0000376 case Tag::start: {
377 bool commandAccepted = true;
Mikhail Naganov98334432022-11-09 02:44:32 +0000378 switch (mState) {
379 case StreamDescriptor::State::STANDBY:
380 mState = StreamDescriptor::State::IDLE;
381 break;
382 case StreamDescriptor::State::PAUSED:
383 mState = StreamDescriptor::State::ACTIVE;
384 break;
385 case StreamDescriptor::State::DRAIN_PAUSED:
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000386 switchToTransientState(StreamDescriptor::State::DRAINING);
Mikhail Naganov30301a42022-09-13 01:20:45 +0000387 break;
388 case StreamDescriptor::State::TRANSFER_PAUSED:
389 switchToTransientState(StreamDescriptor::State::TRANSFERRING);
Mikhail Naganov98334432022-11-09 02:44:32 +0000390 break;
391 default:
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000392 populateReplyWrongState(&reply, command);
Mikhail Naganov30301a42022-09-13 01:20:45 +0000393 commandAccepted = false;
Mikhail Naganov98334432022-11-09 02:44:32 +0000394 }
Mikhail Naganov30301a42022-09-13 01:20:45 +0000395 if (commandAccepted) {
396 populateReply(&reply, mIsConnected);
397 }
398 } break;
Mikhail Naganov98334432022-11-09 02:44:32 +0000399 case Tag::burst:
400 if (const int32_t fmqByteCount = command.get<Tag::burst>(); fmqByteCount >= 0) {
Mikhail Naganov1f72fd42023-02-02 15:26:04 -0800401 LOG(VERBOSE) << __func__ << ": '" << toString(command.getTag()) << "' command for "
402 << fmqByteCount << " bytes";
Mikhail Naganov30301a42022-09-13 01:20:45 +0000403 if (mState != StreamDescriptor::State::ERROR &&
404 mState != StreamDescriptor::State::TRANSFERRING &&
405 mState != StreamDescriptor::State::TRANSFER_PAUSED) {
Mikhail Naganov98334432022-11-09 02:44:32 +0000406 if (!write(fmqByteCount, &reply)) {
407 mState = StreamDescriptor::State::ERROR;
408 }
409 if (mState == StreamDescriptor::State::STANDBY ||
Mikhail Naganov30301a42022-09-13 01:20:45 +0000410 mState == StreamDescriptor::State::DRAIN_PAUSED ||
411 mState == StreamDescriptor::State::PAUSED) {
412 if (mAsyncCallback == nullptr ||
413 mState != StreamDescriptor::State::DRAIN_PAUSED) {
414 mState = StreamDescriptor::State::PAUSED;
415 } else {
416 mState = StreamDescriptor::State::TRANSFER_PAUSED;
417 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000418 } else if (mState == StreamDescriptor::State::IDLE ||
Mikhail Naganov30301a42022-09-13 01:20:45 +0000419 mState == StreamDescriptor::State::DRAINING ||
420 mState == StreamDescriptor::State::ACTIVE) {
421 if (mAsyncCallback == nullptr || reply.fmqByteCount == fmqByteCount) {
422 mState = StreamDescriptor::State::ACTIVE;
423 } else {
424 switchToTransientState(StreamDescriptor::State::TRANSFERRING);
425 }
426 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000427 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000428 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000429 }
430 } else {
431 LOG(WARNING) << __func__ << ": invalid burst byte count: " << fmqByteCount;
432 }
433 break;
434 case Tag::drain:
Mikhail Naganovf429c032023-01-07 00:24:50 +0000435 if (const auto mode = command.get<Tag::drain>();
436 mode == StreamDescriptor::DrainMode::DRAIN_ALL ||
437 mode == StreamDescriptor::DrainMode::DRAIN_EARLY_NOTIFY) {
Mikhail Naganov30301a42022-09-13 01:20:45 +0000438 if (mState == StreamDescriptor::State::ACTIVE ||
439 mState == StreamDescriptor::State::TRANSFERRING) {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000440 if (::android::status_t status = mDriver->drain(mode);
441 status == ::android::OK) {
442 populateReply(&reply, mIsConnected);
443 if (mState == StreamDescriptor::State::ACTIVE && mForceSynchronousDrain) {
444 mState = StreamDescriptor::State::IDLE;
445 } else {
446 switchToTransientState(StreamDescriptor::State::DRAINING);
447 }
Mikhail Naganov194daaa2023-01-05 22:34:20 +0000448 } else {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000449 LOG(ERROR) << __func__ << ": drain failed: " << status;
450 mState = StreamDescriptor::State::ERROR;
Mikhail Naganov194daaa2023-01-05 22:34:20 +0000451 }
Mikhail Naganov30301a42022-09-13 01:20:45 +0000452 } else if (mState == StreamDescriptor::State::TRANSFER_PAUSED) {
453 mState = StreamDescriptor::State::DRAIN_PAUSED;
454 populateReply(&reply, mIsConnected);
455 } else {
456 populateReplyWrongState(&reply, command);
457 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000458 } else {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000459 LOG(WARNING) << __func__ << ": invalid drain mode: " << toString(mode);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000460 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000461 break;
462 case Tag::standby:
Mikhail Naganov98334432022-11-09 02:44:32 +0000463 if (mState == StreamDescriptor::State::IDLE) {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000464 if (::android::status_t status = mDriver->standby(); status == ::android::OK) {
465 populateReply(&reply, mIsConnected);
466 mState = StreamDescriptor::State::STANDBY;
467 } else {
468 LOG(ERROR) << __func__ << ": standby failed: " << status;
469 mState = StreamDescriptor::State::ERROR;
470 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000471 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000472 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000473 }
474 break;
Mikhail Naganov30301a42022-09-13 01:20:45 +0000475 case Tag::pause: {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000476 std::optional<StreamDescriptor::State> nextState;
Mikhail Naganov30301a42022-09-13 01:20:45 +0000477 switch (mState) {
478 case StreamDescriptor::State::ACTIVE:
Mikhail Naganovf429c032023-01-07 00:24:50 +0000479 nextState = StreamDescriptor::State::PAUSED;
Mikhail Naganov30301a42022-09-13 01:20:45 +0000480 break;
481 case StreamDescriptor::State::DRAINING:
Mikhail Naganovf429c032023-01-07 00:24:50 +0000482 nextState = StreamDescriptor::State::DRAIN_PAUSED;
Mikhail Naganov30301a42022-09-13 01:20:45 +0000483 break;
484 case StreamDescriptor::State::TRANSFERRING:
Mikhail Naganovf429c032023-01-07 00:24:50 +0000485 nextState = StreamDescriptor::State::TRANSFER_PAUSED;
Mikhail Naganov30301a42022-09-13 01:20:45 +0000486 break;
487 default:
488 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000489 }
Mikhail Naganovf429c032023-01-07 00:24:50 +0000490 if (nextState.has_value()) {
491 if (::android::status_t status = mDriver->pause(); status == ::android::OK) {
492 populateReply(&reply, mIsConnected);
493 mState = nextState.value();
494 } else {
495 LOG(ERROR) << __func__ << ": pause failed: " << status;
496 mState = StreamDescriptor::State::ERROR;
497 }
Mikhail Naganov30301a42022-09-13 01:20:45 +0000498 }
499 } break;
Mikhail Naganov98334432022-11-09 02:44:32 +0000500 case Tag::flush:
Mikhail Naganov98334432022-11-09 02:44:32 +0000501 if (mState == StreamDescriptor::State::PAUSED ||
Mikhail Naganov30301a42022-09-13 01:20:45 +0000502 mState == StreamDescriptor::State::DRAIN_PAUSED ||
503 mState == StreamDescriptor::State::TRANSFER_PAUSED) {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000504 if (::android::status_t status = mDriver->flush(); status == ::android::OK) {
505 populateReply(&reply, mIsConnected);
506 mState = StreamDescriptor::State::IDLE;
507 } else {
508 LOG(ERROR) << __func__ << ": flush failed: " << status;
509 mState = StreamDescriptor::State::ERROR;
510 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000511 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000512 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000513 }
514 break;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000515 }
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000516 reply.state = mState;
Mikhail Naganov1f72fd42023-02-02 15:26:04 -0800517 LOG(severity) << __func__ << ": writing reply " << reply.toString();
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000518 if (!mReplyMQ->writeBlocking(&reply, 1)) {
519 LOG(ERROR) << __func__ << ": writing of reply " << reply.toString() << " to MQ failed";
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000520 mState = StreamDescriptor::State::ERROR;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000521 return Status::ABORT;
522 }
523 return Status::CONTINUE;
524}
525
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000526bool StreamOutWorkerLogic::write(size_t clientSize, StreamDescriptor::Reply* reply) {
527 const size_t readByteCount = mDataMQ->availableToRead();
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000528 bool fatal = false;
Mikhail Naganovf429c032023-01-07 00:24:50 +0000529 int32_t latency = Module::kLatencyMs;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000530 if (bool success = readByteCount > 0 ? mDataMQ->read(&mDataBuffer[0], readByteCount) : true) {
531 const bool isConnected = mIsConnected;
Mikhail Naganov1f72fd42023-02-02 15:26:04 -0800532 LOG(VERBOSE) << __func__ << ": reading of " << readByteCount << " bytes from data MQ"
533 << " succeeded; connected? " << isConnected;
Mikhail Naganov20047bc2023-01-05 20:16:07 +0000534 // Amount of data that the HAL module is going to actually use.
535 size_t byteCount = std::min({clientSize, readByteCount, mDataBufferSize});
536 if (byteCount >= mFrameSize && mForceTransientBurst) {
537 // In order to prevent the state machine from going to ACTIVE state,
538 // simulate partial write.
539 byteCount -= mFrameSize;
540 }
Mikhail Naganovf429c032023-01-07 00:24:50 +0000541 size_t actualFrameCount = 0;
542 if (isConnected) {
543 if (::android::status_t status = mDriver->transfer(
544 mDataBuffer.get(), byteCount / mFrameSize, &actualFrameCount, &latency);
545 status != ::android::OK) {
546 fatal = true;
547 LOG(ERROR) << __func__ << ": write failed: " << status;
548 }
549 } else {
550 if (mAsyncCallback == nullptr) {
551 usleep(3000); // Simulate blocking transfer delay.
552 }
553 actualFrameCount = byteCount / mFrameSize;
554 }
555 const size_t actualByteCount = actualFrameCount * mFrameSize;
Mikhail Naganov20047bc2023-01-05 20:16:07 +0000556 // Frames are consumed and counted regardless of the connection status.
Mikhail Naganovf429c032023-01-07 00:24:50 +0000557 reply->fmqByteCount += actualByteCount;
558 mFrameCount += actualFrameCount;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000559 populateReply(reply, isConnected);
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000560 } else {
561 LOG(WARNING) << __func__ << ": reading of " << readByteCount
562 << " bytes of data from MQ failed";
563 reply->status = STATUS_NOT_ENOUGH_DATA;
564 }
Mikhail Naganovf429c032023-01-07 00:24:50 +0000565 reply->latencyMs = latency;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000566 return !fatal;
567}
568
Mikhail Naganovf429c032023-01-07 00:24:50 +0000569template <class Metadata>
570StreamCommonImpl<Metadata>::~StreamCommonImpl() {
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000571 if (!isClosed()) {
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000572 LOG(ERROR) << __func__ << ": stream was not closed prior to destruction, resource leak";
573 stopWorker();
574 // The worker and the context should clean up by themselves via destructors.
575 }
576}
577
Mikhail Naganovf429c032023-01-07 00:24:50 +0000578template <class Metadata>
579void StreamCommonImpl<Metadata>::createStreamCommon(
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000580 const std::shared_ptr<StreamCommonInterface>& delegate) {
581 if (mCommon != nullptr) {
582 LOG(FATAL) << __func__ << ": attempting to create the common interface twice";
583 }
584 mCommon = ndk::SharedRefBase::make<StreamCommon>(delegate);
585 mCommonBinder = mCommon->asBinder();
586 AIBinder_setMinSchedulerPolicy(mCommonBinder.get(), SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
587}
588
Mikhail Naganovf429c032023-01-07 00:24:50 +0000589template <class Metadata>
590ndk::ScopedAStatus StreamCommonImpl<Metadata>::getStreamCommon(
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000591 std::shared_ptr<IStreamCommon>* _aidl_return) {
592 if (mCommon == nullptr) {
593 LOG(FATAL) << __func__ << ": the common interface was not created";
594 }
595 *_aidl_return = mCommon;
596 LOG(DEBUG) << __func__ << ": returning " << _aidl_return->get()->asBinder().get();
597 return ndk::ScopedAStatus::ok();
598}
599
Mikhail Naganovf429c032023-01-07 00:24:50 +0000600template <class Metadata>
601ndk::ScopedAStatus StreamCommonImpl<Metadata>::updateHwAvSyncId(int32_t in_hwAvSyncId) {
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000602 LOG(DEBUG) << __func__ << ": id " << in_hwAvSyncId;
603 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
604}
605
Mikhail Naganovf429c032023-01-07 00:24:50 +0000606template <class Metadata>
607ndk::ScopedAStatus StreamCommonImpl<Metadata>::getVendorParameters(
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000608 const std::vector<std::string>& in_ids, std::vector<VendorParameter>* _aidl_return) {
609 LOG(DEBUG) << __func__ << ": id count: " << in_ids.size();
610 (void)_aidl_return;
611 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
612}
613
Mikhail Naganovf429c032023-01-07 00:24:50 +0000614template <class Metadata>
615ndk::ScopedAStatus StreamCommonImpl<Metadata>::setVendorParameters(
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000616 const std::vector<VendorParameter>& in_parameters, bool in_async) {
617 LOG(DEBUG) << __func__ << ": parameters count " << in_parameters.size()
618 << ", async: " << in_async;
619 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
620}
621
Mikhail Naganovf429c032023-01-07 00:24:50 +0000622template <class Metadata>
623ndk::ScopedAStatus StreamCommonImpl<Metadata>::addEffect(
Mikhail Naganovfb1acde2022-12-12 18:57:36 +0000624 const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect) {
625 if (in_effect == nullptr) {
626 LOG(DEBUG) << __func__ << ": null effect";
627 } else {
628 LOG(DEBUG) << __func__ << ": effect Binder" << in_effect->asBinder().get();
629 }
630 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
631}
632
Mikhail Naganovf429c032023-01-07 00:24:50 +0000633template <class Metadata>
634ndk::ScopedAStatus StreamCommonImpl<Metadata>::removeEffect(
Mikhail Naganovfb1acde2022-12-12 18:57:36 +0000635 const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect) {
636 if (in_effect == nullptr) {
637 LOG(DEBUG) << __func__ << ": null effect";
638 } else {
639 LOG(DEBUG) << __func__ << ": effect Binder" << in_effect->asBinder().get();
640 }
641 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
642}
643
Mikhail Naganovf429c032023-01-07 00:24:50 +0000644template <class Metadata>
645ndk::ScopedAStatus StreamCommonImpl<Metadata>::close() {
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000646 LOG(DEBUG) << __func__;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000647 if (!isClosed()) {
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000648 stopWorker();
649 LOG(DEBUG) << __func__ << ": joining the worker thread...";
Mikhail Naganovf429c032023-01-07 00:24:50 +0000650 mWorker->stop();
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000651 LOG(DEBUG) << __func__ << ": worker thread joined";
652 mContext.reset();
Mikhail Naganovf429c032023-01-07 00:24:50 +0000653 mWorker->setClosed();
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000654 return ndk::ScopedAStatus::ok();
655 } else {
656 LOG(ERROR) << __func__ << ": stream was already closed";
657 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
658 }
659}
660
Mikhail Naganovf429c032023-01-07 00:24:50 +0000661template <class Metadata>
662void StreamCommonImpl<Metadata>::stopWorker() {
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000663 if (auto commandMQ = mContext.getCommandMQ(); commandMQ != nullptr) {
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000664 LOG(DEBUG) << __func__ << ": asking the worker to exit...";
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000665 auto cmd = StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::halReservedExit>(
666 mContext.getInternalCommandCookie());
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000667 // Note: never call 'pause' and 'resume' methods of StreamWorker
668 // in the HAL implementation. These methods are to be used by
669 // the client side only. Preventing the worker loop from running
670 // on the HAL side can cause a deadlock.
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000671 if (!commandMQ->writeBlocking(&cmd, 1)) {
672 LOG(ERROR) << __func__ << ": failed to write exit command to the MQ";
673 }
674 LOG(DEBUG) << __func__ << ": done";
675 }
676}
677
Mikhail Naganovf429c032023-01-07 00:24:50 +0000678template <class Metadata>
679ndk::ScopedAStatus StreamCommonImpl<Metadata>::updateMetadata(const Metadata& metadata) {
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000680 LOG(DEBUG) << __func__;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000681 if (!isClosed()) {
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000682 mMetadata = metadata;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000683 return ndk::ScopedAStatus::ok();
684 }
685 LOG(ERROR) << __func__ << ": stream was closed";
686 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
687}
688
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000689// static
Mikhail Naganovf429c032023-01-07 00:24:50 +0000690ndk::ScopedAStatus StreamIn::initInstance(const std::shared_ptr<StreamIn>& stream) {
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000691 if (auto status = stream->init(); !status.isOk()) {
692 return status;
693 }
694 stream->createStreamCommon(stream);
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000695 return ndk::ScopedAStatus::ok();
696}
697
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000698namespace {
699static std::map<AudioDevice, std::string> transformMicrophones(
700 const std::vector<MicrophoneInfo>& microphones) {
701 std::map<AudioDevice, std::string> result;
702 std::transform(microphones.begin(), microphones.end(), std::inserter(result, result.begin()),
703 [](const auto& mic) { return std::make_pair(mic.device, mic.id); });
704 return result;
705}
706} // namespace
707
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000708StreamIn::StreamIn(const SinkMetadata& sinkMetadata, StreamContext&& context,
Mikhail Naganovf429c032023-01-07 00:24:50 +0000709 const DriverInterface::CreateInstance& createDriver,
710 const StreamWorkerInterface::CreateInstance& createWorker,
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000711 const std::vector<MicrophoneInfo>& microphones)
Mikhail Naganovf429c032023-01-07 00:24:50 +0000712 : StreamCommonImpl<SinkMetadata>(sinkMetadata, std::move(context), createDriver, createWorker),
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000713 mMicrophones(transformMicrophones(microphones)) {
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000714 LOG(DEBUG) << __func__;
715}
716
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000717ndk::ScopedAStatus StreamIn::getActiveMicrophones(
718 std::vector<MicrophoneDynamicInfo>* _aidl_return) {
719 std::vector<MicrophoneDynamicInfo> result;
720 std::vector<MicrophoneDynamicInfo::ChannelMapping> channelMapping{
721 getChannelCount(mContext.getChannelLayout()),
722 MicrophoneDynamicInfo::ChannelMapping::DIRECT};
723 for (auto it = mConnectedDevices.begin(); it != mConnectedDevices.end(); ++it) {
724 if (auto micIt = mMicrophones.find(*it); micIt != mMicrophones.end()) {
725 MicrophoneDynamicInfo dynMic;
726 dynMic.id = micIt->second;
727 dynMic.channelMapping = channelMapping;
728 result.push_back(std::move(dynMic));
729 }
730 }
731 *_aidl_return = std::move(result);
732 LOG(DEBUG) << __func__ << ": returning " << ::android::internal::ToString(*_aidl_return);
733 return ndk::ScopedAStatus::ok();
734}
735
736ndk::ScopedAStatus StreamIn::getMicrophoneDirection(MicrophoneDirection* _aidl_return) {
737 LOG(DEBUG) << __func__;
738 (void)_aidl_return;
739 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
740}
741
742ndk::ScopedAStatus StreamIn::setMicrophoneDirection(MicrophoneDirection in_direction) {
743 LOG(DEBUG) << __func__ << ": direction " << toString(in_direction);
744 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
745}
746
747ndk::ScopedAStatus StreamIn::getMicrophoneFieldDimension(float* _aidl_return) {
748 LOG(DEBUG) << __func__;
749 (void)_aidl_return;
750 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
751}
752
753ndk::ScopedAStatus StreamIn::setMicrophoneFieldDimension(float in_zoom) {
754 LOG(DEBUG) << __func__ << ": zoom " << in_zoom;
755 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
756}
757
Mikhail Naganov383cd422022-10-15 00:25:45 +0000758ndk::ScopedAStatus StreamIn::getHwGain(std::vector<float>* _aidl_return) {
759 LOG(DEBUG) << __func__;
760 (void)_aidl_return;
761 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
762}
763
764ndk::ScopedAStatus StreamIn::setHwGain(const std::vector<float>& in_channelGains) {
765 LOG(DEBUG) << __func__ << ": gains " << ::android::internal::ToString(in_channelGains);
766 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
767}
768
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000769// static
Mikhail Naganovf429c032023-01-07 00:24:50 +0000770ndk::ScopedAStatus StreamOut::initInstance(const std::shared_ptr<StreamOut>& stream) {
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000771 if (auto status = stream->init(); !status.isOk()) {
772 return status;
773 }
774 stream->createStreamCommon(stream);
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000775 return ndk::ScopedAStatus::ok();
776}
777
778StreamOut::StreamOut(const SourceMetadata& sourceMetadata, StreamContext&& context,
Mikhail Naganovf429c032023-01-07 00:24:50 +0000779 const DriverInterface::CreateInstance& createDriver,
780 const StreamWorkerInterface::CreateInstance& createWorker,
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000781 const std::optional<AudioOffloadInfo>& offloadInfo)
Mikhail Naganovf429c032023-01-07 00:24:50 +0000782 : StreamCommonImpl<SourceMetadata>(sourceMetadata, std::move(context), createDriver,
783 createWorker),
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000784 mOffloadInfo(offloadInfo) {
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000785 LOG(DEBUG) << __func__;
786}
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000787
Mikhail Naganov383cd422022-10-15 00:25:45 +0000788ndk::ScopedAStatus StreamOut::getHwVolume(std::vector<float>* _aidl_return) {
789 LOG(DEBUG) << __func__;
790 (void)_aidl_return;
791 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
792}
793
794ndk::ScopedAStatus StreamOut::setHwVolume(const std::vector<float>& in_channelVolumes) {
795 LOG(DEBUG) << __func__ << ": gains " << ::android::internal::ToString(in_channelVolumes);
796 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
797}
798
Mikhail Naganov74927202022-12-19 16:37:14 +0000799ndk::ScopedAStatus StreamOut::getAudioDescriptionMixLevel(float* _aidl_return) {
800 LOG(DEBUG) << __func__;
801 (void)_aidl_return;
802 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
803}
804
805ndk::ScopedAStatus StreamOut::setAudioDescriptionMixLevel(float in_leveldB) {
806 LOG(DEBUG) << __func__ << ": description mix level " << in_leveldB;
807 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
808}
809
810ndk::ScopedAStatus StreamOut::getDualMonoMode(AudioDualMonoMode* _aidl_return) {
811 LOG(DEBUG) << __func__;
812 (void)_aidl_return;
813 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
814}
815
816ndk::ScopedAStatus StreamOut::setDualMonoMode(AudioDualMonoMode in_mode) {
817 LOG(DEBUG) << __func__ << ": dual mono mode " << toString(in_mode);
818 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
819}
820
821ndk::ScopedAStatus StreamOut::getRecommendedLatencyModes(
822 std::vector<AudioLatencyMode>* _aidl_return) {
823 LOG(DEBUG) << __func__;
824 (void)_aidl_return;
825 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
826}
827
828ndk::ScopedAStatus StreamOut::setLatencyMode(AudioLatencyMode in_mode) {
829 LOG(DEBUG) << __func__ << ": latency mode " << toString(in_mode);
830 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
831}
832
833ndk::ScopedAStatus StreamOut::getPlaybackRateParameters(AudioPlaybackRate* _aidl_return) {
834 LOG(DEBUG) << __func__;
835 (void)_aidl_return;
836 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
837}
838
839ndk::ScopedAStatus StreamOut::setPlaybackRateParameters(const AudioPlaybackRate& in_playbackRate) {
840 LOG(DEBUG) << __func__ << ": " << in_playbackRate.toString();
841 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
842}
843
844ndk::ScopedAStatus StreamOut::selectPresentation(int32_t in_presentationId, int32_t in_programId) {
845 LOG(DEBUG) << __func__ << ": presentationId " << in_presentationId << ", programId "
846 << in_programId;
847 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
848}
849
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000850} // namespace aidl::android::hardware::audio::core