blob: 25814e445aaa07803b3f518c7d96d9eb5a2564a6 [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 Naganovbd483c02022-11-17 20:33:39 +0000138 LOG(DEBUG) << __func__ << ": received command " << command.toString() << " in " << kThreadName;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000139 StreamDescriptor::Reply reply{};
Mikhail Naganov98334432022-11-09 02:44:32 +0000140 reply.status = STATUS_BAD_VALUE;
141 using Tag = StreamDescriptor::Command::Tag;
142 switch (command.getTag()) {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000143 case Tag::halReservedExit:
144 if (const int32_t cookie = command.get<Tag::halReservedExit>();
Mikhail Naganov98334432022-11-09 02:44:32 +0000145 cookie == mInternalCommandCookie) {
Mikhail Naganov98334432022-11-09 02:44:32 +0000146 setClosed();
147 // This is an internal command, no need to reply.
148 return Status::EXIT;
149 } else {
150 LOG(WARNING) << __func__ << ": EXIT command has a bad cookie: " << cookie;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000151 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000152 break;
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000153 case Tag::getStatus:
154 populateReply(&reply, mIsConnected);
155 break;
Mikhail Naganov98334432022-11-09 02:44:32 +0000156 case Tag::start:
Mikhail Naganov98334432022-11-09 02:44:32 +0000157 if (mState == StreamDescriptor::State::STANDBY ||
158 mState == StreamDescriptor::State::DRAINING) {
159 populateReply(&reply, mIsConnected);
160 mState = mState == StreamDescriptor::State::STANDBY
161 ? StreamDescriptor::State::IDLE
162 : StreamDescriptor::State::ACTIVE;
163 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000164 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000165 }
166 break;
167 case Tag::burst:
168 if (const int32_t fmqByteCount = command.get<Tag::burst>(); fmqByteCount >= 0) {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000169 LOG(DEBUG) << __func__ << ": '" << toString(command.getTag()) << "' command for "
170 << fmqByteCount << " bytes";
Mikhail Naganov98334432022-11-09 02:44:32 +0000171 if (mState == StreamDescriptor::State::IDLE ||
172 mState == StreamDescriptor::State::ACTIVE ||
173 mState == StreamDescriptor::State::PAUSED ||
174 mState == StreamDescriptor::State::DRAINING) {
175 if (!read(fmqByteCount, &reply)) {
176 mState = StreamDescriptor::State::ERROR;
177 }
178 if (mState == StreamDescriptor::State::IDLE ||
179 mState == StreamDescriptor::State::PAUSED) {
180 mState = StreamDescriptor::State::ACTIVE;
181 } else if (mState == StreamDescriptor::State::DRAINING) {
182 // To simplify the reference code, we assume that the read operation
183 // has consumed all the data remaining in the hardware buffer.
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000184 // In a real implementation, here we would either remain in
185 // the 'DRAINING' state, or transfer to 'STANDBY' depending on the
186 // buffer state.
Mikhail Naganov98334432022-11-09 02:44:32 +0000187 mState = StreamDescriptor::State::STANDBY;
188 }
189 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000190 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000191 }
192 } else {
193 LOG(WARNING) << __func__ << ": invalid burst byte count: " << fmqByteCount;
194 }
195 break;
196 case Tag::drain:
Mikhail Naganovf429c032023-01-07 00:24:50 +0000197 if (const auto mode = command.get<Tag::drain>();
198 mode == StreamDescriptor::DrainMode::DRAIN_UNSPECIFIED) {
Mikhail Naganov30301a42022-09-13 01:20:45 +0000199 if (mState == StreamDescriptor::State::ACTIVE) {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000200 if (::android::status_t status = mDriver->drain(mode);
201 status == ::android::OK) {
202 populateReply(&reply, mIsConnected);
203 mState = StreamDescriptor::State::DRAINING;
204 } else {
205 LOG(ERROR) << __func__ << ": drain failed: " << status;
206 mState = StreamDescriptor::State::ERROR;
207 }
Mikhail Naganov30301a42022-09-13 01:20:45 +0000208 } else {
209 populateReplyWrongState(&reply, command);
210 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000211 } else {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000212 LOG(WARNING) << __func__ << ": invalid drain mode: " << toString(mode);
Mikhail Naganov98334432022-11-09 02:44:32 +0000213 }
214 break;
215 case Tag::standby:
Mikhail Naganov98334432022-11-09 02:44:32 +0000216 if (mState == StreamDescriptor::State::IDLE) {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000217 if (::android::status_t status = mDriver->standby(); status == ::android::OK) {
218 populateReply(&reply, mIsConnected);
219 mState = StreamDescriptor::State::STANDBY;
220 } else {
221 LOG(ERROR) << __func__ << ": standby failed: " << status;
222 mState = StreamDescriptor::State::ERROR;
223 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000224 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000225 populateReplyWrongState(&reply, command);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000226 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000227 break;
228 case Tag::pause:
Mikhail Naganov98334432022-11-09 02:44:32 +0000229 if (mState == StreamDescriptor::State::ACTIVE) {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000230 if (::android::status_t status = mDriver->pause(); status == ::android::OK) {
231 populateReply(&reply, mIsConnected);
232 mState = StreamDescriptor::State::PAUSED;
233 } else {
234 LOG(ERROR) << __func__ << ": pause failed: " << status;
235 mState = StreamDescriptor::State::ERROR;
236 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000237 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000238 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000239 }
240 break;
241 case Tag::flush:
Mikhail Naganov98334432022-11-09 02:44:32 +0000242 if (mState == StreamDescriptor::State::PAUSED) {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000243 if (::android::status_t status = mDriver->flush(); status == ::android::OK) {
244 populateReply(&reply, mIsConnected);
245 mState = StreamDescriptor::State::STANDBY;
246 } else {
247 LOG(ERROR) << __func__ << ": flush failed: " << status;
248 mState = StreamDescriptor::State::ERROR;
249 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000250 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000251 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000252 }
253 break;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000254 }
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000255 reply.state = mState;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000256 LOG(DEBUG) << __func__ << ": writing reply " << reply.toString();
257 if (!mReplyMQ->writeBlocking(&reply, 1)) {
258 LOG(ERROR) << __func__ << ": writing of reply " << reply.toString() << " to MQ failed";
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000259 mState = StreamDescriptor::State::ERROR;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000260 return Status::ABORT;
261 }
262 return Status::CONTINUE;
263}
264
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000265bool StreamInWorkerLogic::read(size_t clientSize, StreamDescriptor::Reply* reply) {
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000266 const size_t byteCount = std::min({clientSize, mDataMQ->availableToWrite(), mDataBufferSize});
267 const bool isConnected = mIsConnected;
Mikhail Naganovf429c032023-01-07 00:24:50 +0000268 size_t actualFrameCount = 0;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000269 bool fatal = false;
Mikhail Naganovf429c032023-01-07 00:24:50 +0000270 int32_t latency = Module::kLatencyMs;
271 if (isConnected) {
272 if (::android::status_t status = mDriver->transfer(
273 mDataBuffer.get(), byteCount / mFrameSize, &actualFrameCount, &latency);
274 status != ::android::OK) {
275 fatal = true;
276 LOG(ERROR) << __func__ << ": read failed: " << status;
277 }
278 } else {
279 usleep(3000); // Simulate blocking transfer delay.
280 for (size_t i = 0; i < byteCount; ++i) mDataBuffer[i] = 0;
281 actualFrameCount = byteCount / mFrameSize;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000282 }
Mikhail Naganovf429c032023-01-07 00:24:50 +0000283 const size_t actualByteCount = actualFrameCount * mFrameSize;
284 if (bool success =
285 actualByteCount > 0 ? mDataMQ->write(&mDataBuffer[0], actualByteCount) : true;
286 success) {
287 LOG(DEBUG) << __func__ << ": writing of " << actualByteCount << " bytes into data MQ"
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000288 << " succeeded; connected? " << isConnected;
289 // Frames are provided and counted regardless of connection status.
Mikhail Naganovf429c032023-01-07 00:24:50 +0000290 reply->fmqByteCount += actualByteCount;
291 mFrameCount += actualFrameCount;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000292 populateReply(reply, isConnected);
293 } else {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000294 LOG(WARNING) << __func__ << ": writing of " << actualByteCount
295 << " bytes of data to MQ failed";
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000296 reply->status = STATUS_NOT_ENOUGH_DATA;
297 }
Mikhail Naganovf429c032023-01-07 00:24:50 +0000298 reply->latencyMs = latency;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000299 return !fatal;
300}
301
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000302const std::string StreamOutWorkerLogic::kThreadName = "writer";
303
304StreamOutWorkerLogic::Status StreamOutWorkerLogic::cycle() {
Mikhail Naganov30301a42022-09-13 01:20:45 +0000305 if (mState == StreamDescriptor::State::DRAINING ||
306 mState == StreamDescriptor::State::TRANSFERRING) {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000307 if (auto stateDurationMs = std::chrono::duration_cast<std::chrono::milliseconds>(
308 std::chrono::steady_clock::now() - mTransientStateStart);
309 stateDurationMs >= mTransientStateDelayMs) {
Mikhail Naganov30301a42022-09-13 01:20:45 +0000310 if (mAsyncCallback == nullptr) {
311 // In blocking mode, mState can only be DRAINING.
312 mState = StreamDescriptor::State::IDLE;
313 } else {
314 // In a real implementation, the driver should notify the HAL about
315 // drain or transfer completion. In the stub, we switch unconditionally.
316 if (mState == StreamDescriptor::State::DRAINING) {
317 mState = StreamDescriptor::State::IDLE;
318 ndk::ScopedAStatus status = mAsyncCallback->onDrainReady();
319 if (!status.isOk()) {
320 LOG(ERROR) << __func__ << ": error from onDrainReady: " << status;
321 }
322 } else {
323 mState = StreamDescriptor::State::ACTIVE;
324 ndk::ScopedAStatus status = mAsyncCallback->onTransferReady();
325 if (!status.isOk()) {
326 LOG(ERROR) << __func__ << ": error from onTransferReady: " << status;
327 }
328 }
329 }
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000330 if (mTransientStateDelayMs.count() != 0) {
331 LOG(DEBUG) << __func__ << ": switched to state " << toString(mState)
332 << " after a timeout";
333 }
334 }
335 }
336
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000337 StreamDescriptor::Command command{};
338 if (!mCommandMQ->readBlocking(&command, 1)) {
339 LOG(ERROR) << __func__ << ": reading of command from MQ failed";
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000340 mState = StreamDescriptor::State::ERROR;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000341 return Status::ABORT;
342 }
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000343 LOG(DEBUG) << __func__ << ": received command " << command.toString() << " in " << kThreadName;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000344 StreamDescriptor::Reply reply{};
Mikhail Naganov98334432022-11-09 02:44:32 +0000345 reply.status = STATUS_BAD_VALUE;
346 using Tag = StreamDescriptor::Command::Tag;
347 switch (command.getTag()) {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000348 case Tag::halReservedExit:
349 if (const int32_t cookie = command.get<Tag::halReservedExit>();
Mikhail Naganov98334432022-11-09 02:44:32 +0000350 cookie == mInternalCommandCookie) {
Mikhail Naganov98334432022-11-09 02:44:32 +0000351 setClosed();
352 // This is an internal command, no need to reply.
353 return Status::EXIT;
354 } else {
355 LOG(WARNING) << __func__ << ": EXIT command has a bad cookie: " << cookie;
356 }
357 break;
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000358 case Tag::getStatus:
359 populateReply(&reply, mIsConnected);
360 break;
Mikhail Naganov30301a42022-09-13 01:20:45 +0000361 case Tag::start: {
362 bool commandAccepted = true;
Mikhail Naganov98334432022-11-09 02:44:32 +0000363 switch (mState) {
364 case StreamDescriptor::State::STANDBY:
365 mState = StreamDescriptor::State::IDLE;
366 break;
367 case StreamDescriptor::State::PAUSED:
368 mState = StreamDescriptor::State::ACTIVE;
369 break;
370 case StreamDescriptor::State::DRAIN_PAUSED:
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000371 switchToTransientState(StreamDescriptor::State::DRAINING);
Mikhail Naganov30301a42022-09-13 01:20:45 +0000372 break;
373 case StreamDescriptor::State::TRANSFER_PAUSED:
374 switchToTransientState(StreamDescriptor::State::TRANSFERRING);
Mikhail Naganov98334432022-11-09 02:44:32 +0000375 break;
376 default:
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000377 populateReplyWrongState(&reply, command);
Mikhail Naganov30301a42022-09-13 01:20:45 +0000378 commandAccepted = false;
Mikhail Naganov98334432022-11-09 02:44:32 +0000379 }
Mikhail Naganov30301a42022-09-13 01:20:45 +0000380 if (commandAccepted) {
381 populateReply(&reply, mIsConnected);
382 }
383 } break;
Mikhail Naganov98334432022-11-09 02:44:32 +0000384 case Tag::burst:
385 if (const int32_t fmqByteCount = command.get<Tag::burst>(); fmqByteCount >= 0) {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000386 LOG(DEBUG) << __func__ << ": '" << toString(command.getTag()) << "' command for "
387 << fmqByteCount << " bytes";
Mikhail Naganov30301a42022-09-13 01:20:45 +0000388 if (mState != StreamDescriptor::State::ERROR &&
389 mState != StreamDescriptor::State::TRANSFERRING &&
390 mState != StreamDescriptor::State::TRANSFER_PAUSED) {
Mikhail Naganov98334432022-11-09 02:44:32 +0000391 if (!write(fmqByteCount, &reply)) {
392 mState = StreamDescriptor::State::ERROR;
393 }
394 if (mState == StreamDescriptor::State::STANDBY ||
Mikhail Naganov30301a42022-09-13 01:20:45 +0000395 mState == StreamDescriptor::State::DRAIN_PAUSED ||
396 mState == StreamDescriptor::State::PAUSED) {
397 if (mAsyncCallback == nullptr ||
398 mState != StreamDescriptor::State::DRAIN_PAUSED) {
399 mState = StreamDescriptor::State::PAUSED;
400 } else {
401 mState = StreamDescriptor::State::TRANSFER_PAUSED;
402 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000403 } else if (mState == StreamDescriptor::State::IDLE ||
Mikhail Naganov30301a42022-09-13 01:20:45 +0000404 mState == StreamDescriptor::State::DRAINING ||
405 mState == StreamDescriptor::State::ACTIVE) {
406 if (mAsyncCallback == nullptr || reply.fmqByteCount == fmqByteCount) {
407 mState = StreamDescriptor::State::ACTIVE;
408 } else {
409 switchToTransientState(StreamDescriptor::State::TRANSFERRING);
410 }
411 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000412 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000413 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000414 }
415 } else {
416 LOG(WARNING) << __func__ << ": invalid burst byte count: " << fmqByteCount;
417 }
418 break;
419 case Tag::drain:
Mikhail Naganovf429c032023-01-07 00:24:50 +0000420 if (const auto mode = command.get<Tag::drain>();
421 mode == StreamDescriptor::DrainMode::DRAIN_ALL ||
422 mode == StreamDescriptor::DrainMode::DRAIN_EARLY_NOTIFY) {
Mikhail Naganov30301a42022-09-13 01:20:45 +0000423 if (mState == StreamDescriptor::State::ACTIVE ||
424 mState == StreamDescriptor::State::TRANSFERRING) {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000425 if (::android::status_t status = mDriver->drain(mode);
426 status == ::android::OK) {
427 populateReply(&reply, mIsConnected);
428 if (mState == StreamDescriptor::State::ACTIVE && mForceSynchronousDrain) {
429 mState = StreamDescriptor::State::IDLE;
430 } else {
431 switchToTransientState(StreamDescriptor::State::DRAINING);
432 }
Mikhail Naganov194daaa2023-01-05 22:34:20 +0000433 } else {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000434 LOG(ERROR) << __func__ << ": drain failed: " << status;
435 mState = StreamDescriptor::State::ERROR;
Mikhail Naganov194daaa2023-01-05 22:34:20 +0000436 }
Mikhail Naganov30301a42022-09-13 01:20:45 +0000437 } else if (mState == StreamDescriptor::State::TRANSFER_PAUSED) {
438 mState = StreamDescriptor::State::DRAIN_PAUSED;
439 populateReply(&reply, mIsConnected);
440 } else {
441 populateReplyWrongState(&reply, command);
442 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000443 } else {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000444 LOG(WARNING) << __func__ << ": invalid drain mode: " << toString(mode);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000445 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000446 break;
447 case Tag::standby:
Mikhail Naganov98334432022-11-09 02:44:32 +0000448 if (mState == StreamDescriptor::State::IDLE) {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000449 if (::android::status_t status = mDriver->standby(); status == ::android::OK) {
450 populateReply(&reply, mIsConnected);
451 mState = StreamDescriptor::State::STANDBY;
452 } else {
453 LOG(ERROR) << __func__ << ": standby failed: " << status;
454 mState = StreamDescriptor::State::ERROR;
455 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000456 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000457 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000458 }
459 break;
Mikhail Naganov30301a42022-09-13 01:20:45 +0000460 case Tag::pause: {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000461 std::optional<StreamDescriptor::State> nextState;
Mikhail Naganov30301a42022-09-13 01:20:45 +0000462 switch (mState) {
463 case StreamDescriptor::State::ACTIVE:
Mikhail Naganovf429c032023-01-07 00:24:50 +0000464 nextState = StreamDescriptor::State::PAUSED;
Mikhail Naganov30301a42022-09-13 01:20:45 +0000465 break;
466 case StreamDescriptor::State::DRAINING:
Mikhail Naganovf429c032023-01-07 00:24:50 +0000467 nextState = StreamDescriptor::State::DRAIN_PAUSED;
Mikhail Naganov30301a42022-09-13 01:20:45 +0000468 break;
469 case StreamDescriptor::State::TRANSFERRING:
Mikhail Naganovf429c032023-01-07 00:24:50 +0000470 nextState = StreamDescriptor::State::TRANSFER_PAUSED;
Mikhail Naganov30301a42022-09-13 01:20:45 +0000471 break;
472 default:
473 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000474 }
Mikhail Naganovf429c032023-01-07 00:24:50 +0000475 if (nextState.has_value()) {
476 if (::android::status_t status = mDriver->pause(); status == ::android::OK) {
477 populateReply(&reply, mIsConnected);
478 mState = nextState.value();
479 } else {
480 LOG(ERROR) << __func__ << ": pause failed: " << status;
481 mState = StreamDescriptor::State::ERROR;
482 }
Mikhail Naganov30301a42022-09-13 01:20:45 +0000483 }
484 } break;
Mikhail Naganov98334432022-11-09 02:44:32 +0000485 case Tag::flush:
Mikhail Naganov98334432022-11-09 02:44:32 +0000486 if (mState == StreamDescriptor::State::PAUSED ||
Mikhail Naganov30301a42022-09-13 01:20:45 +0000487 mState == StreamDescriptor::State::DRAIN_PAUSED ||
488 mState == StreamDescriptor::State::TRANSFER_PAUSED) {
Mikhail Naganovf429c032023-01-07 00:24:50 +0000489 if (::android::status_t status = mDriver->flush(); status == ::android::OK) {
490 populateReply(&reply, mIsConnected);
491 mState = StreamDescriptor::State::IDLE;
492 } else {
493 LOG(ERROR) << __func__ << ": flush failed: " << status;
494 mState = StreamDescriptor::State::ERROR;
495 }
Mikhail Naganov98334432022-11-09 02:44:32 +0000496 } else {
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000497 populateReplyWrongState(&reply, command);
Mikhail Naganov98334432022-11-09 02:44:32 +0000498 }
499 break;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000500 }
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000501 reply.state = mState;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000502 LOG(DEBUG) << __func__ << ": writing reply " << reply.toString();
503 if (!mReplyMQ->writeBlocking(&reply, 1)) {
504 LOG(ERROR) << __func__ << ": writing of reply " << reply.toString() << " to MQ failed";
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000505 mState = StreamDescriptor::State::ERROR;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000506 return Status::ABORT;
507 }
508 return Status::CONTINUE;
509}
510
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000511bool StreamOutWorkerLogic::write(size_t clientSize, StreamDescriptor::Reply* reply) {
512 const size_t readByteCount = mDataMQ->availableToRead();
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000513 bool fatal = false;
Mikhail Naganovf429c032023-01-07 00:24:50 +0000514 int32_t latency = Module::kLatencyMs;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000515 if (bool success = readByteCount > 0 ? mDataMQ->read(&mDataBuffer[0], readByteCount) : true) {
516 const bool isConnected = mIsConnected;
517 LOG(DEBUG) << __func__ << ": reading of " << readByteCount << " bytes from data MQ"
518 << " succeeded; connected? " << isConnected;
Mikhail Naganov20047bc2023-01-05 20:16:07 +0000519 // Amount of data that the HAL module is going to actually use.
520 size_t byteCount = std::min({clientSize, readByteCount, mDataBufferSize});
521 if (byteCount >= mFrameSize && mForceTransientBurst) {
522 // In order to prevent the state machine from going to ACTIVE state,
523 // simulate partial write.
524 byteCount -= mFrameSize;
525 }
Mikhail Naganovf429c032023-01-07 00:24:50 +0000526 size_t actualFrameCount = 0;
527 if (isConnected) {
528 if (::android::status_t status = mDriver->transfer(
529 mDataBuffer.get(), byteCount / mFrameSize, &actualFrameCount, &latency);
530 status != ::android::OK) {
531 fatal = true;
532 LOG(ERROR) << __func__ << ": write failed: " << status;
533 }
534 } else {
535 if (mAsyncCallback == nullptr) {
536 usleep(3000); // Simulate blocking transfer delay.
537 }
538 actualFrameCount = byteCount / mFrameSize;
539 }
540 const size_t actualByteCount = actualFrameCount * mFrameSize;
Mikhail Naganov20047bc2023-01-05 20:16:07 +0000541 // Frames are consumed and counted regardless of the connection status.
Mikhail Naganovf429c032023-01-07 00:24:50 +0000542 reply->fmqByteCount += actualByteCount;
543 mFrameCount += actualFrameCount;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000544 populateReply(reply, isConnected);
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000545 } else {
546 LOG(WARNING) << __func__ << ": reading of " << readByteCount
547 << " bytes of data from MQ failed";
548 reply->status = STATUS_NOT_ENOUGH_DATA;
549 }
Mikhail Naganovf429c032023-01-07 00:24:50 +0000550 reply->latencyMs = latency;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000551 return !fatal;
552}
553
Mikhail Naganovf429c032023-01-07 00:24:50 +0000554template <class Metadata>
555StreamCommonImpl<Metadata>::~StreamCommonImpl() {
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000556 if (!isClosed()) {
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000557 LOG(ERROR) << __func__ << ": stream was not closed prior to destruction, resource leak";
558 stopWorker();
559 // The worker and the context should clean up by themselves via destructors.
560 }
561}
562
Mikhail Naganovf429c032023-01-07 00:24:50 +0000563template <class Metadata>
564void StreamCommonImpl<Metadata>::createStreamCommon(
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000565 const std::shared_ptr<StreamCommonInterface>& delegate) {
566 if (mCommon != nullptr) {
567 LOG(FATAL) << __func__ << ": attempting to create the common interface twice";
568 }
569 mCommon = ndk::SharedRefBase::make<StreamCommon>(delegate);
570 mCommonBinder = mCommon->asBinder();
571 AIBinder_setMinSchedulerPolicy(mCommonBinder.get(), SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
572}
573
Mikhail Naganovf429c032023-01-07 00:24:50 +0000574template <class Metadata>
575ndk::ScopedAStatus StreamCommonImpl<Metadata>::getStreamCommon(
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000576 std::shared_ptr<IStreamCommon>* _aidl_return) {
577 if (mCommon == nullptr) {
578 LOG(FATAL) << __func__ << ": the common interface was not created";
579 }
580 *_aidl_return = mCommon;
581 LOG(DEBUG) << __func__ << ": returning " << _aidl_return->get()->asBinder().get();
582 return ndk::ScopedAStatus::ok();
583}
584
Mikhail Naganovf429c032023-01-07 00:24:50 +0000585template <class Metadata>
586ndk::ScopedAStatus StreamCommonImpl<Metadata>::updateHwAvSyncId(int32_t in_hwAvSyncId) {
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000587 LOG(DEBUG) << __func__ << ": id " << in_hwAvSyncId;
588 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
589}
590
Mikhail Naganovf429c032023-01-07 00:24:50 +0000591template <class Metadata>
592ndk::ScopedAStatus StreamCommonImpl<Metadata>::getVendorParameters(
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000593 const std::vector<std::string>& in_ids, std::vector<VendorParameter>* _aidl_return) {
594 LOG(DEBUG) << __func__ << ": id count: " << in_ids.size();
595 (void)_aidl_return;
596 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
597}
598
Mikhail Naganovf429c032023-01-07 00:24:50 +0000599template <class Metadata>
600ndk::ScopedAStatus StreamCommonImpl<Metadata>::setVendorParameters(
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000601 const std::vector<VendorParameter>& in_parameters, bool in_async) {
602 LOG(DEBUG) << __func__ << ": parameters count " << in_parameters.size()
603 << ", async: " << in_async;
604 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
605}
606
Mikhail Naganovf429c032023-01-07 00:24:50 +0000607template <class Metadata>
608ndk::ScopedAStatus StreamCommonImpl<Metadata>::addEffect(
Mikhail Naganovfb1acde2022-12-12 18:57:36 +0000609 const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect) {
610 if (in_effect == nullptr) {
611 LOG(DEBUG) << __func__ << ": null effect";
612 } else {
613 LOG(DEBUG) << __func__ << ": effect Binder" << in_effect->asBinder().get();
614 }
615 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
616}
617
Mikhail Naganovf429c032023-01-07 00:24:50 +0000618template <class Metadata>
619ndk::ScopedAStatus StreamCommonImpl<Metadata>::removeEffect(
Mikhail Naganovfb1acde2022-12-12 18:57:36 +0000620 const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect) {
621 if (in_effect == nullptr) {
622 LOG(DEBUG) << __func__ << ": null effect";
623 } else {
624 LOG(DEBUG) << __func__ << ": effect Binder" << in_effect->asBinder().get();
625 }
626 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
627}
628
Mikhail Naganovf429c032023-01-07 00:24:50 +0000629template <class Metadata>
630ndk::ScopedAStatus StreamCommonImpl<Metadata>::close() {
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000631 LOG(DEBUG) << __func__;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000632 if (!isClosed()) {
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000633 stopWorker();
634 LOG(DEBUG) << __func__ << ": joining the worker thread...";
Mikhail Naganovf429c032023-01-07 00:24:50 +0000635 mWorker->stop();
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000636 LOG(DEBUG) << __func__ << ": worker thread joined";
637 mContext.reset();
Mikhail Naganovf429c032023-01-07 00:24:50 +0000638 mWorker->setClosed();
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000639 return ndk::ScopedAStatus::ok();
640 } else {
641 LOG(ERROR) << __func__ << ": stream was already closed";
642 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
643 }
644}
645
Mikhail Naganovf429c032023-01-07 00:24:50 +0000646template <class Metadata>
647void StreamCommonImpl<Metadata>::stopWorker() {
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000648 if (auto commandMQ = mContext.getCommandMQ(); commandMQ != nullptr) {
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000649 LOG(DEBUG) << __func__ << ": asking the worker to exit...";
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000650 auto cmd = StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::halReservedExit>(
651 mContext.getInternalCommandCookie());
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000652 // Note: never call 'pause' and 'resume' methods of StreamWorker
653 // in the HAL implementation. These methods are to be used by
654 // the client side only. Preventing the worker loop from running
655 // on the HAL side can cause a deadlock.
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000656 if (!commandMQ->writeBlocking(&cmd, 1)) {
657 LOG(ERROR) << __func__ << ": failed to write exit command to the MQ";
658 }
659 LOG(DEBUG) << __func__ << ": done";
660 }
661}
662
Mikhail Naganovf429c032023-01-07 00:24:50 +0000663template <class Metadata>
664ndk::ScopedAStatus StreamCommonImpl<Metadata>::updateMetadata(const Metadata& metadata) {
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000665 LOG(DEBUG) << __func__;
Mikhail Naganovcce8e5f2022-09-13 01:20:45 +0000666 if (!isClosed()) {
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000667 mMetadata = metadata;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000668 return ndk::ScopedAStatus::ok();
669 }
670 LOG(ERROR) << __func__ << ": stream was closed";
671 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
672}
673
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000674// static
Mikhail Naganovf429c032023-01-07 00:24:50 +0000675ndk::ScopedAStatus StreamIn::initInstance(const std::shared_ptr<StreamIn>& stream) {
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000676 if (auto status = stream->init(); !status.isOk()) {
677 return status;
678 }
679 stream->createStreamCommon(stream);
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000680 return ndk::ScopedAStatus::ok();
681}
682
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000683namespace {
684static std::map<AudioDevice, std::string> transformMicrophones(
685 const std::vector<MicrophoneInfo>& microphones) {
686 std::map<AudioDevice, std::string> result;
687 std::transform(microphones.begin(), microphones.end(), std::inserter(result, result.begin()),
688 [](const auto& mic) { return std::make_pair(mic.device, mic.id); });
689 return result;
690}
691} // namespace
692
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000693StreamIn::StreamIn(const SinkMetadata& sinkMetadata, StreamContext&& context,
Mikhail Naganovf429c032023-01-07 00:24:50 +0000694 const DriverInterface::CreateInstance& createDriver,
695 const StreamWorkerInterface::CreateInstance& createWorker,
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000696 const std::vector<MicrophoneInfo>& microphones)
Mikhail Naganovf429c032023-01-07 00:24:50 +0000697 : StreamCommonImpl<SinkMetadata>(sinkMetadata, std::move(context), createDriver, createWorker),
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000698 mMicrophones(transformMicrophones(microphones)) {
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000699 LOG(DEBUG) << __func__;
700}
701
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000702ndk::ScopedAStatus StreamIn::getActiveMicrophones(
703 std::vector<MicrophoneDynamicInfo>* _aidl_return) {
704 std::vector<MicrophoneDynamicInfo> result;
705 std::vector<MicrophoneDynamicInfo::ChannelMapping> channelMapping{
706 getChannelCount(mContext.getChannelLayout()),
707 MicrophoneDynamicInfo::ChannelMapping::DIRECT};
708 for (auto it = mConnectedDevices.begin(); it != mConnectedDevices.end(); ++it) {
709 if (auto micIt = mMicrophones.find(*it); micIt != mMicrophones.end()) {
710 MicrophoneDynamicInfo dynMic;
711 dynMic.id = micIt->second;
712 dynMic.channelMapping = channelMapping;
713 result.push_back(std::move(dynMic));
714 }
715 }
716 *_aidl_return = std::move(result);
717 LOG(DEBUG) << __func__ << ": returning " << ::android::internal::ToString(*_aidl_return);
718 return ndk::ScopedAStatus::ok();
719}
720
721ndk::ScopedAStatus StreamIn::getMicrophoneDirection(MicrophoneDirection* _aidl_return) {
722 LOG(DEBUG) << __func__;
723 (void)_aidl_return;
724 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
725}
726
727ndk::ScopedAStatus StreamIn::setMicrophoneDirection(MicrophoneDirection in_direction) {
728 LOG(DEBUG) << __func__ << ": direction " << toString(in_direction);
729 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
730}
731
732ndk::ScopedAStatus StreamIn::getMicrophoneFieldDimension(float* _aidl_return) {
733 LOG(DEBUG) << __func__;
734 (void)_aidl_return;
735 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
736}
737
738ndk::ScopedAStatus StreamIn::setMicrophoneFieldDimension(float in_zoom) {
739 LOG(DEBUG) << __func__ << ": zoom " << in_zoom;
740 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
741}
742
Mikhail Naganov383cd422022-10-15 00:25:45 +0000743ndk::ScopedAStatus StreamIn::getHwGain(std::vector<float>* _aidl_return) {
744 LOG(DEBUG) << __func__;
745 (void)_aidl_return;
746 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
747}
748
749ndk::ScopedAStatus StreamIn::setHwGain(const std::vector<float>& in_channelGains) {
750 LOG(DEBUG) << __func__ << ": gains " << ::android::internal::ToString(in_channelGains);
751 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
752}
753
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000754// static
Mikhail Naganovf429c032023-01-07 00:24:50 +0000755ndk::ScopedAStatus StreamOut::initInstance(const std::shared_ptr<StreamOut>& stream) {
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000756 if (auto status = stream->init(); !status.isOk()) {
757 return status;
758 }
759 stream->createStreamCommon(stream);
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000760 return ndk::ScopedAStatus::ok();
761}
762
763StreamOut::StreamOut(const SourceMetadata& sourceMetadata, StreamContext&& context,
Mikhail Naganovf429c032023-01-07 00:24:50 +0000764 const DriverInterface::CreateInstance& createDriver,
765 const StreamWorkerInterface::CreateInstance& createWorker,
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000766 const std::optional<AudioOffloadInfo>& offloadInfo)
Mikhail Naganovf429c032023-01-07 00:24:50 +0000767 : StreamCommonImpl<SourceMetadata>(sourceMetadata, std::move(context), createDriver,
768 createWorker),
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000769 mOffloadInfo(offloadInfo) {
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000770 LOG(DEBUG) << __func__;
771}
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000772
Mikhail Naganov383cd422022-10-15 00:25:45 +0000773ndk::ScopedAStatus StreamOut::getHwVolume(std::vector<float>* _aidl_return) {
774 LOG(DEBUG) << __func__;
775 (void)_aidl_return;
776 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
777}
778
779ndk::ScopedAStatus StreamOut::setHwVolume(const std::vector<float>& in_channelVolumes) {
780 LOG(DEBUG) << __func__ << ": gains " << ::android::internal::ToString(in_channelVolumes);
781 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
782}
783
Mikhail Naganov74927202022-12-19 16:37:14 +0000784ndk::ScopedAStatus StreamOut::getAudioDescriptionMixLevel(float* _aidl_return) {
785 LOG(DEBUG) << __func__;
786 (void)_aidl_return;
787 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
788}
789
790ndk::ScopedAStatus StreamOut::setAudioDescriptionMixLevel(float in_leveldB) {
791 LOG(DEBUG) << __func__ << ": description mix level " << in_leveldB;
792 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
793}
794
795ndk::ScopedAStatus StreamOut::getDualMonoMode(AudioDualMonoMode* _aidl_return) {
796 LOG(DEBUG) << __func__;
797 (void)_aidl_return;
798 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
799}
800
801ndk::ScopedAStatus StreamOut::setDualMonoMode(AudioDualMonoMode in_mode) {
802 LOG(DEBUG) << __func__ << ": dual mono mode " << toString(in_mode);
803 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
804}
805
806ndk::ScopedAStatus StreamOut::getRecommendedLatencyModes(
807 std::vector<AudioLatencyMode>* _aidl_return) {
808 LOG(DEBUG) << __func__;
809 (void)_aidl_return;
810 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
811}
812
813ndk::ScopedAStatus StreamOut::setLatencyMode(AudioLatencyMode in_mode) {
814 LOG(DEBUG) << __func__ << ": latency mode " << toString(in_mode);
815 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
816}
817
818ndk::ScopedAStatus StreamOut::getPlaybackRateParameters(AudioPlaybackRate* _aidl_return) {
819 LOG(DEBUG) << __func__;
820 (void)_aidl_return;
821 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
822}
823
824ndk::ScopedAStatus StreamOut::setPlaybackRateParameters(const AudioPlaybackRate& in_playbackRate) {
825 LOG(DEBUG) << __func__ << ": " << in_playbackRate.toString();
826 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
827}
828
829ndk::ScopedAStatus StreamOut::selectPresentation(int32_t in_presentationId, int32_t in_programId) {
830 LOG(DEBUG) << __func__ << ": presentationId " << in_presentationId << ", programId "
831 << in_programId;
832 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
833}
834
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000835} // namespace aidl::android::hardware::audio::core