blob: d62ca1d1ca6703953aa0fab1c5ed5414ec5944e5 [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 Naganovef6bc742022-10-06 00:14:19 +000034using android::hardware::audio::common::getChannelCount;
35using android::hardware::audio::common::getFrameSizeInBytes;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000036
37namespace aidl::android::hardware::audio::core {
38
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +000039void StreamContext::fillDescriptor(StreamDescriptor* desc) {
40 if (mCommandMQ) {
41 desc->command = mCommandMQ->dupeDesc();
42 }
43 if (mReplyMQ) {
44 desc->reply = mReplyMQ->dupeDesc();
45 }
46 if (mDataMQ) {
Mikhail Naganovef6bc742022-10-06 00:14:19 +000047 const size_t frameSize = getFrameSize();
48 desc->frameSizeBytes = frameSize;
49 desc->bufferSizeFrames = mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize() / frameSize;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +000050 desc->audio.set<StreamDescriptor::AudioBuffer::Tag::fmq>(mDataMQ->dupeDesc());
51 }
Mikhail Naganov6a4872d2022-06-15 21:39:04 +000052}
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000053
Mikhail Naganovef6bc742022-10-06 00:14:19 +000054size_t StreamContext::getFrameSize() const {
55 return getFrameSizeInBytes(mFormat, mChannelLayout);
56}
57
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +000058bool StreamContext::isValid() const {
59 if (mCommandMQ && !mCommandMQ->isValid()) {
60 LOG(ERROR) << "command FMQ is invalid";
61 return false;
62 }
63 if (mReplyMQ && !mReplyMQ->isValid()) {
64 LOG(ERROR) << "reply FMQ is invalid";
65 return false;
66 }
Mikhail Naganovef6bc742022-10-06 00:14:19 +000067 if (getFrameSize() == 0) {
68 LOG(ERROR) << "frame size is invalid";
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +000069 return false;
70 }
71 if (mDataMQ && !mDataMQ->isValid()) {
72 LOG(ERROR) << "data FMQ is invalid";
73 return false;
74 }
75 return true;
76}
77
78void StreamContext::reset() {
79 mCommandMQ.reset();
80 mReplyMQ.reset();
81 mDataMQ.reset();
82}
83
84std::string StreamWorkerCommonLogic::init() {
85 if (mCommandMQ == nullptr) return "Command MQ is null";
86 if (mReplyMQ == nullptr) return "Reply MQ is null";
87 if (mDataMQ == nullptr) return "Data MQ is null";
Mikhail Naganovf429c032023-01-07 00:24:50 +000088 if (sizeof(DataBufferElement) != mDataMQ->getQuantumSize()) {
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +000089 return "Unexpected Data MQ quantum size: " + std::to_string(mDataMQ->getQuantumSize());
90 }
91 mDataBufferSize = mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize();
Mikhail Naganovf429c032023-01-07 00:24:50 +000092 mDataBuffer.reset(new (std::nothrow) DataBufferElement[mDataBufferSize]);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +000093 if (mDataBuffer == nullptr) {
94 return "Failed to allocate data buffer for element count " +
95 std::to_string(mDataMQ->getQuantumCount()) +
96 ", size in bytes: " + std::to_string(mDataBufferSize);
97 }
Mikhail Naganovf429c032023-01-07 00:24:50 +000098 if (::android::status_t status = mDriver->init(); status != STATUS_OK) {
99 return "Failed to initialize the driver: " + std::to_string(status);
100 }
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000101 return "";
102}
103
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000104void StreamWorkerCommonLogic::populateReply(StreamDescriptor::Reply* reply,
105 bool isConnected) const {
Mikhail Naganov549a8222022-11-23 18:30:07 +0000106 reply->status = STATUS_OK;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000107 if (isConnected) {
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000108 reply->observable.frames = mFrameCount;
109 reply->observable.timeNs = ::android::elapsedRealtimeNano();
110 } else {
Mikhail Naganov549a8222022-11-23 18:30:07 +0000111 reply->observable.frames = StreamDescriptor::Position::UNKNOWN;
112 reply->observable.timeNs = StreamDescriptor::Position::UNKNOWN;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000113 }
114}
115
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000116void StreamWorkerCommonLogic::populateReplyWrongState(
117 StreamDescriptor::Reply* reply, const StreamDescriptor::Command& command) const {
118 LOG(WARNING) << "command '" << toString(command.getTag())
119 << "' can not be handled in the state " << toString(mState);
120 reply->status = STATUS_INVALID_OPERATION;
121}
122
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000123const std::string StreamInWorkerLogic::kThreadName = "reader";
124
125StreamInWorkerLogic::Status StreamInWorkerLogic::cycle() {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000126 // Note: for input streams, draining is driven by the client, thus
127 // "empty buffer" condition can only happen while handling the 'burst'
128 // command. Thus, unlike for output streams, it does not make sense to
129 // delay the 'DRAINING' state here by 'mTransientStateDelayMs'.
130 // TODO: Add a delay for transitions of async operations when/if they added.
131
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000132 StreamDescriptor::Command command{};
133 if (!mCommandMQ->readBlocking(&command, 1)) {
134 LOG(ERROR) << __func__ << ": reading of command from MQ failed";
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000135 mState = StreamDescriptor::State::ERROR;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000136 return Status::ABORT;
137 }
Mikhail Naganov1f72fd42023-02-02 15:26:04 -0800138 using Tag = StreamDescriptor::Command::Tag;
139 using LogSeverity = ::android::base::LogSeverity;
140 const LogSeverity severity =
141 command.getTag() == Tag::burst || command.getTag() == Tag::getStatus
142 ? LogSeverity::VERBOSE
143 : LogSeverity::DEBUG;
144 LOG(severity) << __func__ << ": received command " << command.toString() << " in "
145 << kThreadName;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000146 StreamDescriptor::Reply reply{};
Mikhail Naganov98334432022-11-09 02:44:32 +0000147 reply.status = STATUS_BAD_VALUE;
Mikhail Naganov98334432022-11-09 02:44:32 +0000148 switch (command.getTag()) {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000149 case Tag::halReservedExit:
150 if (const int32_t cookie = command.get<Tag::halReservedExit>();
Mikhail Naganov98334432022-11-09 02:44:32 +0000151 cookie == mInternalCommandCookie) {
Mikhail Naganov98334432022-11-09 02:44:32 +0000152 setClosed();
153 // This is an internal command, no need to reply.
154 return Status::EXIT;
155 } else {
156 LOG(WARNING) << __func__ << ": EXIT command has a bad cookie: " << cookie;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000157 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000158 break;
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000159 case Tag::getStatus:
160 populateReply(&reply, mIsConnected);
161 break;
Mikhail Naganov98334432022-11-09 02:44:32 +0000162 case Tag::start:
Mikhail Naganov98334432022-11-09 02:44:32 +0000163 if (mState == StreamDescriptor::State::STANDBY ||
164 mState == StreamDescriptor::State::DRAINING) {
165 populateReply(&reply, mIsConnected);
166 mState = mState == StreamDescriptor::State::STANDBY
167 ? StreamDescriptor::State::IDLE
168 : StreamDescriptor::State::ACTIVE;
169 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000170 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000171 }
172 break;
173 case Tag::burst:
174 if (const int32_t fmqByteCount = command.get<Tag::burst>(); fmqByteCount >= 0) {
Mikhail Naganov1f72fd42023-02-02 15:26:04 -0800175 LOG(VERBOSE) << __func__ << ": '" << toString(command.getTag()) << "' command for "
176 << fmqByteCount << " bytes";
Mikhail Naganov98334432022-11-09 02:44:32 +0000177 if (mState == StreamDescriptor::State::IDLE ||
178 mState == StreamDescriptor::State::ACTIVE ||
179 mState == StreamDescriptor::State::PAUSED ||
180 mState == StreamDescriptor::State::DRAINING) {
181 if (!read(fmqByteCount, &reply)) {
182 mState = StreamDescriptor::State::ERROR;
183 }
184 if (mState == StreamDescriptor::State::IDLE ||
185 mState == StreamDescriptor::State::PAUSED) {
186 mState = StreamDescriptor::State::ACTIVE;
187 } else if (mState == StreamDescriptor::State::DRAINING) {
188 // To simplify the reference code, we assume that the read operation
189 // has consumed all the data remaining in the hardware buffer.
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000190 // In a real implementation, here we would either remain in
191 // the 'DRAINING' state, or transfer to 'STANDBY' depending on the
192 // buffer state.
Mikhail Naganov98334432022-11-09 02:44:32 +0000193 mState = StreamDescriptor::State::STANDBY;
194 }
195 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000196 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000197 }
198 } else {
199 LOG(WARNING) << __func__ << ": invalid burst byte count: " << fmqByteCount;
200 }
201 break;
202 case Tag::drain:
Mikhail Naganovf429c032023-01-07 00:24:50 +0000203 if (const auto mode = command.get<Tag::drain>();
204 mode == StreamDescriptor::DrainMode::DRAIN_UNSPECIFIED) {
Mikhail Naganov30301a42022-09-13 01:20:45 +0000205 if (mState == StreamDescriptor::State::ACTIVE) {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000206 if (::android::status_t status = mDriver->drain(mode);
207 status == ::android::OK) {
208 populateReply(&reply, mIsConnected);
209 mState = StreamDescriptor::State::DRAINING;
210 } else {
211 LOG(ERROR) << __func__ << ": drain failed: " << status;
212 mState = StreamDescriptor::State::ERROR;
213 }
Mikhail Naganov30301a42022-09-13 01:20:45 +0000214 } else {
215 populateReplyWrongState(&reply, command);
216 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000217 } else {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000218 LOG(WARNING) << __func__ << ": invalid drain mode: " << toString(mode);
Mikhail Naganov98334432022-11-09 02:44:32 +0000219 }
220 break;
221 case Tag::standby:
Mikhail Naganov98334432022-11-09 02:44:32 +0000222 if (mState == StreamDescriptor::State::IDLE) {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000223 if (::android::status_t status = mDriver->standby(); status == ::android::OK) {
224 populateReply(&reply, mIsConnected);
225 mState = StreamDescriptor::State::STANDBY;
226 } else {
227 LOG(ERROR) << __func__ << ": standby failed: " << status;
228 mState = StreamDescriptor::State::ERROR;
229 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000230 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000231 populateReplyWrongState(&reply, command);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000232 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000233 break;
234 case Tag::pause:
Mikhail Naganov98334432022-11-09 02:44:32 +0000235 if (mState == StreamDescriptor::State::ACTIVE) {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000236 if (::android::status_t status = mDriver->pause(); status == ::android::OK) {
237 populateReply(&reply, mIsConnected);
238 mState = StreamDescriptor::State::PAUSED;
239 } else {
240 LOG(ERROR) << __func__ << ": pause failed: " << status;
241 mState = StreamDescriptor::State::ERROR;
242 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000243 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000244 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000245 }
246 break;
247 case Tag::flush:
Mikhail Naganov98334432022-11-09 02:44:32 +0000248 if (mState == StreamDescriptor::State::PAUSED) {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000249 if (::android::status_t status = mDriver->flush(); status == ::android::OK) {
250 populateReply(&reply, mIsConnected);
251 mState = StreamDescriptor::State::STANDBY;
252 } else {
253 LOG(ERROR) << __func__ << ": flush failed: " << status;
254 mState = StreamDescriptor::State::ERROR;
255 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000256 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000257 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000258 }
259 break;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000260 }
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000261 reply.state = mState;
Mikhail Naganov1f72fd42023-02-02 15:26:04 -0800262 LOG(severity) << __func__ << ": writing reply " << reply.toString();
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000263 if (!mReplyMQ->writeBlocking(&reply, 1)) {
264 LOG(ERROR) << __func__ << ": writing of reply " << reply.toString() << " to MQ failed";
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000265 mState = StreamDescriptor::State::ERROR;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000266 return Status::ABORT;
267 }
268 return Status::CONTINUE;
269}
270
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000271bool StreamInWorkerLogic::read(size_t clientSize, StreamDescriptor::Reply* reply) {
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000272 const size_t byteCount = std::min({clientSize, mDataMQ->availableToWrite(), mDataBufferSize});
273 const bool isConnected = mIsConnected;
Mikhail Naganovf429c032023-01-07 00:24:50 +0000274 size_t actualFrameCount = 0;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000275 bool fatal = false;
Mikhail Naganovf429c032023-01-07 00:24:50 +0000276 int32_t latency = Module::kLatencyMs;
277 if (isConnected) {
278 if (::android::status_t status = mDriver->transfer(
279 mDataBuffer.get(), byteCount / mFrameSize, &actualFrameCount, &latency);
280 status != ::android::OK) {
281 fatal = true;
282 LOG(ERROR) << __func__ << ": read failed: " << status;
283 }
284 } else {
285 usleep(3000); // Simulate blocking transfer delay.
286 for (size_t i = 0; i < byteCount; ++i) mDataBuffer[i] = 0;
287 actualFrameCount = byteCount / mFrameSize;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000288 }
Mikhail Naganovf429c032023-01-07 00:24:50 +0000289 const size_t actualByteCount = actualFrameCount * mFrameSize;
290 if (bool success =
291 actualByteCount > 0 ? mDataMQ->write(&mDataBuffer[0], actualByteCount) : true;
292 success) {
Mikhail Naganov1f72fd42023-02-02 15:26:04 -0800293 LOG(VERBOSE) << __func__ << ": writing of " << actualByteCount << " bytes into data MQ"
294 << " succeeded; connected? " << isConnected;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000295 // Frames are provided and counted regardless of connection status.
Mikhail Naganovf429c032023-01-07 00:24:50 +0000296 reply->fmqByteCount += actualByteCount;
297 mFrameCount += actualFrameCount;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000298 populateReply(reply, isConnected);
299 } else {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000300 LOG(WARNING) << __func__ << ": writing of " << actualByteCount
301 << " bytes of data to MQ failed";
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000302 reply->status = STATUS_NOT_ENOUGH_DATA;
303 }
Mikhail Naganovf429c032023-01-07 00:24:50 +0000304 reply->latencyMs = latency;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000305 return !fatal;
306}
307
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000308const std::string StreamOutWorkerLogic::kThreadName = "writer";
309
310StreamOutWorkerLogic::Status StreamOutWorkerLogic::cycle() {
Mikhail Naganov30301a42022-09-13 01:20:45 +0000311 if (mState == StreamDescriptor::State::DRAINING ||
312 mState == StreamDescriptor::State::TRANSFERRING) {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000313 if (auto stateDurationMs = std::chrono::duration_cast<std::chrono::milliseconds>(
314 std::chrono::steady_clock::now() - mTransientStateStart);
315 stateDurationMs >= mTransientStateDelayMs) {
Mikhail Naganov30301a42022-09-13 01:20:45 +0000316 if (mAsyncCallback == nullptr) {
317 // In blocking mode, mState can only be DRAINING.
318 mState = StreamDescriptor::State::IDLE;
319 } else {
320 // In a real implementation, the driver should notify the HAL about
321 // drain or transfer completion. In the stub, we switch unconditionally.
322 if (mState == StreamDescriptor::State::DRAINING) {
323 mState = StreamDescriptor::State::IDLE;
324 ndk::ScopedAStatus status = mAsyncCallback->onDrainReady();
325 if (!status.isOk()) {
326 LOG(ERROR) << __func__ << ": error from onDrainReady: " << status;
327 }
328 } else {
329 mState = StreamDescriptor::State::ACTIVE;
330 ndk::ScopedAStatus status = mAsyncCallback->onTransferReady();
331 if (!status.isOk()) {
332 LOG(ERROR) << __func__ << ": error from onTransferReady: " << status;
333 }
334 }
335 }
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000336 if (mTransientStateDelayMs.count() != 0) {
337 LOG(DEBUG) << __func__ << ": switched to state " << toString(mState)
338 << " after a timeout";
339 }
340 }
341 }
342
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000343 StreamDescriptor::Command command{};
344 if (!mCommandMQ->readBlocking(&command, 1)) {
345 LOG(ERROR) << __func__ << ": reading of command from MQ failed";
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000346 mState = StreamDescriptor::State::ERROR;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000347 return Status::ABORT;
348 }
Mikhail Naganov1f72fd42023-02-02 15:26:04 -0800349 using Tag = StreamDescriptor::Command::Tag;
350 using LogSeverity = ::android::base::LogSeverity;
351 const LogSeverity severity =
352 command.getTag() == Tag::burst || command.getTag() == Tag::getStatus
353 ? LogSeverity::VERBOSE
354 : LogSeverity::DEBUG;
355 LOG(severity) << __func__ << ": received command " << command.toString() << " in "
356 << kThreadName;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000357 StreamDescriptor::Reply reply{};
Mikhail Naganov98334432022-11-09 02:44:32 +0000358 reply.status = STATUS_BAD_VALUE;
359 using Tag = StreamDescriptor::Command::Tag;
360 switch (command.getTag()) {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000361 case Tag::halReservedExit:
362 if (const int32_t cookie = command.get<Tag::halReservedExit>();
Mikhail Naganov98334432022-11-09 02:44:32 +0000363 cookie == mInternalCommandCookie) {
Mikhail Naganov98334432022-11-09 02:44:32 +0000364 setClosed();
365 // This is an internal command, no need to reply.
366 return Status::EXIT;
367 } else {
368 LOG(WARNING) << __func__ << ": EXIT command has a bad cookie: " << cookie;
369 }
370 break;
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000371 case Tag::getStatus:
372 populateReply(&reply, mIsConnected);
373 break;
Mikhail Naganov30301a42022-09-13 01:20:45 +0000374 case Tag::start: {
375 bool commandAccepted = true;
Mikhail Naganov98334432022-11-09 02:44:32 +0000376 switch (mState) {
377 case StreamDescriptor::State::STANDBY:
378 mState = StreamDescriptor::State::IDLE;
379 break;
380 case StreamDescriptor::State::PAUSED:
381 mState = StreamDescriptor::State::ACTIVE;
382 break;
383 case StreamDescriptor::State::DRAIN_PAUSED:
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000384 switchToTransientState(StreamDescriptor::State::DRAINING);
Mikhail Naganov30301a42022-09-13 01:20:45 +0000385 break;
386 case StreamDescriptor::State::TRANSFER_PAUSED:
387 switchToTransientState(StreamDescriptor::State::TRANSFERRING);
Mikhail Naganov98334432022-11-09 02:44:32 +0000388 break;
389 default:
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000390 populateReplyWrongState(&reply, command);
Mikhail Naganov30301a42022-09-13 01:20:45 +0000391 commandAccepted = false;
Mikhail Naganov98334432022-11-09 02:44:32 +0000392 }
Mikhail Naganov30301a42022-09-13 01:20:45 +0000393 if (commandAccepted) {
394 populateReply(&reply, mIsConnected);
395 }
396 } break;
Mikhail Naganov98334432022-11-09 02:44:32 +0000397 case Tag::burst:
398 if (const int32_t fmqByteCount = command.get<Tag::burst>(); fmqByteCount >= 0) {
Mikhail Naganov1f72fd42023-02-02 15:26:04 -0800399 LOG(VERBOSE) << __func__ << ": '" << toString(command.getTag()) << "' command for "
400 << fmqByteCount << " bytes";
Mikhail Naganov30301a42022-09-13 01:20:45 +0000401 if (mState != StreamDescriptor::State::ERROR &&
402 mState != StreamDescriptor::State::TRANSFERRING &&
403 mState != StreamDescriptor::State::TRANSFER_PAUSED) {
Mikhail Naganov98334432022-11-09 02:44:32 +0000404 if (!write(fmqByteCount, &reply)) {
405 mState = StreamDescriptor::State::ERROR;
406 }
407 if (mState == StreamDescriptor::State::STANDBY ||
Mikhail Naganov30301a42022-09-13 01:20:45 +0000408 mState == StreamDescriptor::State::DRAIN_PAUSED ||
409 mState == StreamDescriptor::State::PAUSED) {
410 if (mAsyncCallback == nullptr ||
411 mState != StreamDescriptor::State::DRAIN_PAUSED) {
412 mState = StreamDescriptor::State::PAUSED;
413 } else {
414 mState = StreamDescriptor::State::TRANSFER_PAUSED;
415 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000416 } else if (mState == StreamDescriptor::State::IDLE ||
Mikhail Naganov30301a42022-09-13 01:20:45 +0000417 mState == StreamDescriptor::State::DRAINING ||
418 mState == StreamDescriptor::State::ACTIVE) {
419 if (mAsyncCallback == nullptr || reply.fmqByteCount == fmqByteCount) {
420 mState = StreamDescriptor::State::ACTIVE;
421 } else {
422 switchToTransientState(StreamDescriptor::State::TRANSFERRING);
423 }
424 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000425 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000426 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000427 }
428 } else {
429 LOG(WARNING) << __func__ << ": invalid burst byte count: " << fmqByteCount;
430 }
431 break;
432 case Tag::drain:
Mikhail Naganovf429c032023-01-07 00:24:50 +0000433 if (const auto mode = command.get<Tag::drain>();
434 mode == StreamDescriptor::DrainMode::DRAIN_ALL ||
435 mode == StreamDescriptor::DrainMode::DRAIN_EARLY_NOTIFY) {
Mikhail Naganov30301a42022-09-13 01:20:45 +0000436 if (mState == StreamDescriptor::State::ACTIVE ||
437 mState == StreamDescriptor::State::TRANSFERRING) {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000438 if (::android::status_t status = mDriver->drain(mode);
439 status == ::android::OK) {
440 populateReply(&reply, mIsConnected);
441 if (mState == StreamDescriptor::State::ACTIVE && mForceSynchronousDrain) {
442 mState = StreamDescriptor::State::IDLE;
443 } else {
444 switchToTransientState(StreamDescriptor::State::DRAINING);
445 }
Mikhail Naganov194daaa2023-01-05 22:34:20 +0000446 } else {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000447 LOG(ERROR) << __func__ << ": drain failed: " << status;
448 mState = StreamDescriptor::State::ERROR;
Mikhail Naganov194daaa2023-01-05 22:34:20 +0000449 }
Mikhail Naganov30301a42022-09-13 01:20:45 +0000450 } else if (mState == StreamDescriptor::State::TRANSFER_PAUSED) {
451 mState = StreamDescriptor::State::DRAIN_PAUSED;
452 populateReply(&reply, mIsConnected);
453 } else {
454 populateReplyWrongState(&reply, command);
455 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000456 } else {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000457 LOG(WARNING) << __func__ << ": invalid drain mode: " << toString(mode);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000458 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000459 break;
460 case Tag::standby:
Mikhail Naganov98334432022-11-09 02:44:32 +0000461 if (mState == StreamDescriptor::State::IDLE) {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000462 if (::android::status_t status = mDriver->standby(); status == ::android::OK) {
463 populateReply(&reply, mIsConnected);
464 mState = StreamDescriptor::State::STANDBY;
465 } else {
466 LOG(ERROR) << __func__ << ": standby failed: " << status;
467 mState = StreamDescriptor::State::ERROR;
468 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000469 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000470 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000471 }
472 break;
Mikhail Naganov30301a42022-09-13 01:20:45 +0000473 case Tag::pause: {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000474 std::optional<StreamDescriptor::State> nextState;
Mikhail Naganov30301a42022-09-13 01:20:45 +0000475 switch (mState) {
476 case StreamDescriptor::State::ACTIVE:
Mikhail Naganovf429c032023-01-07 00:24:50 +0000477 nextState = StreamDescriptor::State::PAUSED;
Mikhail Naganov30301a42022-09-13 01:20:45 +0000478 break;
479 case StreamDescriptor::State::DRAINING:
Mikhail Naganovf429c032023-01-07 00:24:50 +0000480 nextState = StreamDescriptor::State::DRAIN_PAUSED;
Mikhail Naganov30301a42022-09-13 01:20:45 +0000481 break;
482 case StreamDescriptor::State::TRANSFERRING:
Mikhail Naganovf429c032023-01-07 00:24:50 +0000483 nextState = StreamDescriptor::State::TRANSFER_PAUSED;
Mikhail Naganov30301a42022-09-13 01:20:45 +0000484 break;
485 default:
486 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000487 }
Mikhail Naganovf429c032023-01-07 00:24:50 +0000488 if (nextState.has_value()) {
489 if (::android::status_t status = mDriver->pause(); status == ::android::OK) {
490 populateReply(&reply, mIsConnected);
491 mState = nextState.value();
492 } else {
493 LOG(ERROR) << __func__ << ": pause failed: " << status;
494 mState = StreamDescriptor::State::ERROR;
495 }
Mikhail Naganov30301a42022-09-13 01:20:45 +0000496 }
497 } break;
Mikhail Naganov98334432022-11-09 02:44:32 +0000498 case Tag::flush:
Mikhail Naganov98334432022-11-09 02:44:32 +0000499 if (mState == StreamDescriptor::State::PAUSED ||
Mikhail Naganov30301a42022-09-13 01:20:45 +0000500 mState == StreamDescriptor::State::DRAIN_PAUSED ||
501 mState == StreamDescriptor::State::TRANSFER_PAUSED) {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000502 if (::android::status_t status = mDriver->flush(); status == ::android::OK) {
503 populateReply(&reply, mIsConnected);
504 mState = StreamDescriptor::State::IDLE;
505 } else {
506 LOG(ERROR) << __func__ << ": flush failed: " << status;
507 mState = StreamDescriptor::State::ERROR;
508 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000509 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000510 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000511 }
512 break;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000513 }
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000514 reply.state = mState;
Mikhail Naganov1f72fd42023-02-02 15:26:04 -0800515 LOG(severity) << __func__ << ": writing reply " << reply.toString();
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000516 if (!mReplyMQ->writeBlocking(&reply, 1)) {
517 LOG(ERROR) << __func__ << ": writing of reply " << reply.toString() << " to MQ failed";
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000518 mState = StreamDescriptor::State::ERROR;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000519 return Status::ABORT;
520 }
521 return Status::CONTINUE;
522}
523
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000524bool StreamOutWorkerLogic::write(size_t clientSize, StreamDescriptor::Reply* reply) {
525 const size_t readByteCount = mDataMQ->availableToRead();
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000526 bool fatal = false;
Mikhail Naganovf429c032023-01-07 00:24:50 +0000527 int32_t latency = Module::kLatencyMs;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000528 if (bool success = readByteCount > 0 ? mDataMQ->read(&mDataBuffer[0], readByteCount) : true) {
529 const bool isConnected = mIsConnected;
Mikhail Naganov1f72fd42023-02-02 15:26:04 -0800530 LOG(VERBOSE) << __func__ << ": reading of " << readByteCount << " bytes from data MQ"
531 << " succeeded; connected? " << isConnected;
Mikhail Naganov20047bc2023-01-05 20:16:07 +0000532 // Amount of data that the HAL module is going to actually use.
533 size_t byteCount = std::min({clientSize, readByteCount, mDataBufferSize});
534 if (byteCount >= mFrameSize && mForceTransientBurst) {
535 // In order to prevent the state machine from going to ACTIVE state,
536 // simulate partial write.
537 byteCount -= mFrameSize;
538 }
Mikhail Naganovf429c032023-01-07 00:24:50 +0000539 size_t actualFrameCount = 0;
540 if (isConnected) {
541 if (::android::status_t status = mDriver->transfer(
542 mDataBuffer.get(), byteCount / mFrameSize, &actualFrameCount, &latency);
543 status != ::android::OK) {
544 fatal = true;
545 LOG(ERROR) << __func__ << ": write failed: " << status;
546 }
547 } else {
548 if (mAsyncCallback == nullptr) {
549 usleep(3000); // Simulate blocking transfer delay.
550 }
551 actualFrameCount = byteCount / mFrameSize;
552 }
553 const size_t actualByteCount = actualFrameCount * mFrameSize;
Mikhail Naganov20047bc2023-01-05 20:16:07 +0000554 // Frames are consumed and counted regardless of the connection status.
Mikhail Naganovf429c032023-01-07 00:24:50 +0000555 reply->fmqByteCount += actualByteCount;
556 mFrameCount += actualFrameCount;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000557 populateReply(reply, isConnected);
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000558 } else {
559 LOG(WARNING) << __func__ << ": reading of " << readByteCount
560 << " bytes of data from MQ failed";
561 reply->status = STATUS_NOT_ENOUGH_DATA;
562 }
Mikhail Naganovf429c032023-01-07 00:24:50 +0000563 reply->latencyMs = latency;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000564 return !fatal;
565}
566
Mikhail Naganovf429c032023-01-07 00:24:50 +0000567template <class Metadata>
568StreamCommonImpl<Metadata>::~StreamCommonImpl() {
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000569 if (!isClosed()) {
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000570 LOG(ERROR) << __func__ << ": stream was not closed prior to destruction, resource leak";
571 stopWorker();
572 // The worker and the context should clean up by themselves via destructors.
573 }
574}
575
Mikhail Naganovf429c032023-01-07 00:24:50 +0000576template <class Metadata>
577void StreamCommonImpl<Metadata>::createStreamCommon(
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000578 const std::shared_ptr<StreamCommonInterface>& delegate) {
579 if (mCommon != nullptr) {
580 LOG(FATAL) << __func__ << ": attempting to create the common interface twice";
581 }
582 mCommon = ndk::SharedRefBase::make<StreamCommon>(delegate);
583 mCommonBinder = mCommon->asBinder();
584 AIBinder_setMinSchedulerPolicy(mCommonBinder.get(), SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
585}
586
Mikhail Naganovf429c032023-01-07 00:24:50 +0000587template <class Metadata>
588ndk::ScopedAStatus StreamCommonImpl<Metadata>::getStreamCommon(
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000589 std::shared_ptr<IStreamCommon>* _aidl_return) {
590 if (mCommon == nullptr) {
591 LOG(FATAL) << __func__ << ": the common interface was not created";
592 }
593 *_aidl_return = mCommon;
594 LOG(DEBUG) << __func__ << ": returning " << _aidl_return->get()->asBinder().get();
595 return ndk::ScopedAStatus::ok();
596}
597
Mikhail Naganovf429c032023-01-07 00:24:50 +0000598template <class Metadata>
599ndk::ScopedAStatus StreamCommonImpl<Metadata>::updateHwAvSyncId(int32_t in_hwAvSyncId) {
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000600 LOG(DEBUG) << __func__ << ": id " << in_hwAvSyncId;
601 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
602}
603
Mikhail Naganovf429c032023-01-07 00:24:50 +0000604template <class Metadata>
605ndk::ScopedAStatus StreamCommonImpl<Metadata>::getVendorParameters(
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000606 const std::vector<std::string>& in_ids, std::vector<VendorParameter>* _aidl_return) {
607 LOG(DEBUG) << __func__ << ": id count: " << in_ids.size();
608 (void)_aidl_return;
609 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
610}
611
Mikhail Naganovf429c032023-01-07 00:24:50 +0000612template <class Metadata>
613ndk::ScopedAStatus StreamCommonImpl<Metadata>::setVendorParameters(
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000614 const std::vector<VendorParameter>& in_parameters, bool in_async) {
615 LOG(DEBUG) << __func__ << ": parameters count " << in_parameters.size()
616 << ", async: " << in_async;
617 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
618}
619
Mikhail Naganovf429c032023-01-07 00:24:50 +0000620template <class Metadata>
621ndk::ScopedAStatus StreamCommonImpl<Metadata>::addEffect(
Mikhail Naganovfb1acde2022-12-12 18:57:36 +0000622 const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect) {
623 if (in_effect == nullptr) {
624 LOG(DEBUG) << __func__ << ": null effect";
625 } else {
626 LOG(DEBUG) << __func__ << ": effect Binder" << in_effect->asBinder().get();
627 }
628 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
629}
630
Mikhail Naganovf429c032023-01-07 00:24:50 +0000631template <class Metadata>
632ndk::ScopedAStatus StreamCommonImpl<Metadata>::removeEffect(
Mikhail Naganovfb1acde2022-12-12 18:57:36 +0000633 const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect) {
634 if (in_effect == nullptr) {
635 LOG(DEBUG) << __func__ << ": null effect";
636 } else {
637 LOG(DEBUG) << __func__ << ": effect Binder" << in_effect->asBinder().get();
638 }
639 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
640}
641
Mikhail Naganovf429c032023-01-07 00:24:50 +0000642template <class Metadata>
643ndk::ScopedAStatus StreamCommonImpl<Metadata>::close() {
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000644 LOG(DEBUG) << __func__;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000645 if (!isClosed()) {
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000646 stopWorker();
647 LOG(DEBUG) << __func__ << ": joining the worker thread...";
Mikhail Naganovf429c032023-01-07 00:24:50 +0000648 mWorker->stop();
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000649 LOG(DEBUG) << __func__ << ": worker thread joined";
650 mContext.reset();
Mikhail Naganovf429c032023-01-07 00:24:50 +0000651 mWorker->setClosed();
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000652 return ndk::ScopedAStatus::ok();
653 } else {
654 LOG(ERROR) << __func__ << ": stream was already closed";
655 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
656 }
657}
658
Mikhail Naganovf429c032023-01-07 00:24:50 +0000659template <class Metadata>
660void StreamCommonImpl<Metadata>::stopWorker() {
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000661 if (auto commandMQ = mContext.getCommandMQ(); commandMQ != nullptr) {
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000662 LOG(DEBUG) << __func__ << ": asking the worker to exit...";
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000663 auto cmd = StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::halReservedExit>(
664 mContext.getInternalCommandCookie());
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000665 // Note: never call 'pause' and 'resume' methods of StreamWorker
666 // in the HAL implementation. These methods are to be used by
667 // the client side only. Preventing the worker loop from running
668 // on the HAL side can cause a deadlock.
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000669 if (!commandMQ->writeBlocking(&cmd, 1)) {
670 LOG(ERROR) << __func__ << ": failed to write exit command to the MQ";
671 }
672 LOG(DEBUG) << __func__ << ": done";
673 }
674}
675
Mikhail Naganovf429c032023-01-07 00:24:50 +0000676template <class Metadata>
677ndk::ScopedAStatus StreamCommonImpl<Metadata>::updateMetadata(const Metadata& metadata) {
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000678 LOG(DEBUG) << __func__;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000679 if (!isClosed()) {
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000680 mMetadata = metadata;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000681 return ndk::ScopedAStatus::ok();
682 }
683 LOG(ERROR) << __func__ << ": stream was closed";
684 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
685}
686
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000687// static
Mikhail Naganovf429c032023-01-07 00:24:50 +0000688ndk::ScopedAStatus StreamIn::initInstance(const std::shared_ptr<StreamIn>& stream) {
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000689 if (auto status = stream->init(); !status.isOk()) {
690 return status;
691 }
692 stream->createStreamCommon(stream);
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000693 return ndk::ScopedAStatus::ok();
694}
695
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000696namespace {
697static std::map<AudioDevice, std::string> transformMicrophones(
698 const std::vector<MicrophoneInfo>& microphones) {
699 std::map<AudioDevice, std::string> result;
700 std::transform(microphones.begin(), microphones.end(), std::inserter(result, result.begin()),
701 [](const auto& mic) { return std::make_pair(mic.device, mic.id); });
702 return result;
703}
704} // namespace
705
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000706StreamIn::StreamIn(const SinkMetadata& sinkMetadata, StreamContext&& context,
Mikhail Naganovf429c032023-01-07 00:24:50 +0000707 const DriverInterface::CreateInstance& createDriver,
708 const StreamWorkerInterface::CreateInstance& createWorker,
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000709 const std::vector<MicrophoneInfo>& microphones)
Mikhail Naganovf429c032023-01-07 00:24:50 +0000710 : StreamCommonImpl<SinkMetadata>(sinkMetadata, std::move(context), createDriver, createWorker),
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000711 mMicrophones(transformMicrophones(microphones)) {
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000712 LOG(DEBUG) << __func__;
713}
714
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000715ndk::ScopedAStatus StreamIn::getActiveMicrophones(
716 std::vector<MicrophoneDynamicInfo>* _aidl_return) {
717 std::vector<MicrophoneDynamicInfo> result;
718 std::vector<MicrophoneDynamicInfo::ChannelMapping> channelMapping{
719 getChannelCount(mContext.getChannelLayout()),
720 MicrophoneDynamicInfo::ChannelMapping::DIRECT};
721 for (auto it = mConnectedDevices.begin(); it != mConnectedDevices.end(); ++it) {
722 if (auto micIt = mMicrophones.find(*it); micIt != mMicrophones.end()) {
723 MicrophoneDynamicInfo dynMic;
724 dynMic.id = micIt->second;
725 dynMic.channelMapping = channelMapping;
726 result.push_back(std::move(dynMic));
727 }
728 }
729 *_aidl_return = std::move(result);
730 LOG(DEBUG) << __func__ << ": returning " << ::android::internal::ToString(*_aidl_return);
731 return ndk::ScopedAStatus::ok();
732}
733
734ndk::ScopedAStatus StreamIn::getMicrophoneDirection(MicrophoneDirection* _aidl_return) {
735 LOG(DEBUG) << __func__;
736 (void)_aidl_return;
737 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
738}
739
740ndk::ScopedAStatus StreamIn::setMicrophoneDirection(MicrophoneDirection in_direction) {
741 LOG(DEBUG) << __func__ << ": direction " << toString(in_direction);
742 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
743}
744
745ndk::ScopedAStatus StreamIn::getMicrophoneFieldDimension(float* _aidl_return) {
746 LOG(DEBUG) << __func__;
747 (void)_aidl_return;
748 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
749}
750
751ndk::ScopedAStatus StreamIn::setMicrophoneFieldDimension(float in_zoom) {
752 LOG(DEBUG) << __func__ << ": zoom " << in_zoom;
753 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
754}
755
Mikhail Naganov383cd422022-10-15 00:25:45 +0000756ndk::ScopedAStatus StreamIn::getHwGain(std::vector<float>* _aidl_return) {
757 LOG(DEBUG) << __func__;
758 (void)_aidl_return;
759 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
760}
761
762ndk::ScopedAStatus StreamIn::setHwGain(const std::vector<float>& in_channelGains) {
763 LOG(DEBUG) << __func__ << ": gains " << ::android::internal::ToString(in_channelGains);
764 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
765}
766
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000767// static
Mikhail Naganovf429c032023-01-07 00:24:50 +0000768ndk::ScopedAStatus StreamOut::initInstance(const std::shared_ptr<StreamOut>& stream) {
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000769 if (auto status = stream->init(); !status.isOk()) {
770 return status;
771 }
772 stream->createStreamCommon(stream);
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000773 return ndk::ScopedAStatus::ok();
774}
775
776StreamOut::StreamOut(const SourceMetadata& sourceMetadata, StreamContext&& context,
Mikhail Naganovf429c032023-01-07 00:24:50 +0000777 const DriverInterface::CreateInstance& createDriver,
778 const StreamWorkerInterface::CreateInstance& createWorker,
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000779 const std::optional<AudioOffloadInfo>& offloadInfo)
Mikhail Naganovf429c032023-01-07 00:24:50 +0000780 : StreamCommonImpl<SourceMetadata>(sourceMetadata, std::move(context), createDriver,
781 createWorker),
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000782 mOffloadInfo(offloadInfo) {
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000783 LOG(DEBUG) << __func__;
784}
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000785
Mikhail Naganov383cd422022-10-15 00:25:45 +0000786ndk::ScopedAStatus StreamOut::getHwVolume(std::vector<float>* _aidl_return) {
787 LOG(DEBUG) << __func__;
788 (void)_aidl_return;
789 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
790}
791
792ndk::ScopedAStatus StreamOut::setHwVolume(const std::vector<float>& in_channelVolumes) {
793 LOG(DEBUG) << __func__ << ": gains " << ::android::internal::ToString(in_channelVolumes);
794 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
795}
796
Mikhail Naganov74927202022-12-19 16:37:14 +0000797ndk::ScopedAStatus StreamOut::getAudioDescriptionMixLevel(float* _aidl_return) {
798 LOG(DEBUG) << __func__;
799 (void)_aidl_return;
800 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
801}
802
803ndk::ScopedAStatus StreamOut::setAudioDescriptionMixLevel(float in_leveldB) {
804 LOG(DEBUG) << __func__ << ": description mix level " << in_leveldB;
805 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
806}
807
808ndk::ScopedAStatus StreamOut::getDualMonoMode(AudioDualMonoMode* _aidl_return) {
809 LOG(DEBUG) << __func__;
810 (void)_aidl_return;
811 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
812}
813
814ndk::ScopedAStatus StreamOut::setDualMonoMode(AudioDualMonoMode in_mode) {
815 LOG(DEBUG) << __func__ << ": dual mono mode " << toString(in_mode);
816 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
817}
818
819ndk::ScopedAStatus StreamOut::getRecommendedLatencyModes(
820 std::vector<AudioLatencyMode>* _aidl_return) {
821 LOG(DEBUG) << __func__;
822 (void)_aidl_return;
823 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
824}
825
826ndk::ScopedAStatus StreamOut::setLatencyMode(AudioLatencyMode in_mode) {
827 LOG(DEBUG) << __func__ << ": latency mode " << toString(in_mode);
828 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
829}
830
831ndk::ScopedAStatus StreamOut::getPlaybackRateParameters(AudioPlaybackRate* _aidl_return) {
832 LOG(DEBUG) << __func__;
833 (void)_aidl_return;
834 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
835}
836
837ndk::ScopedAStatus StreamOut::setPlaybackRateParameters(const AudioPlaybackRate& in_playbackRate) {
838 LOG(DEBUG) << __func__ << ": " << in_playbackRate.toString();
839 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
840}
841
842ndk::ScopedAStatus StreamOut::selectPresentation(int32_t in_presentationId, int32_t in_programId) {
843 LOG(DEBUG) << __func__ << ": presentationId " << in_presentationId << ", programId "
844 << in_programId;
845 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
846}
847
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000848} // namespace aidl::android::hardware::audio::core