blob: 46e384eabd03453442d5580bdde13853d1020ebb [file] [log] [blame]
Mikhail Naganovcf824f62023-07-24 14:51:36 -07001/*
2 * Copyright (C) 2023 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
Mikhail Naganovcf824f62023-07-24 14:51:36 -070017#define LOG_TAG "AHAL_StreamPrimary"
Weilin Xu29e51682024-10-02 17:16:32 +000018
19#include <cstdio>
20
Mikhail Naganovcf824f62023-07-24 14:51:36 -070021#include <android-base/logging.h>
Weilin Xu29e51682024-10-02 17:16:32 +000022#include <android-base/parseint.h>
Mikhail Naganovcf824f62023-07-24 14:51:36 -070023#include <android-base/properties.h>
Mikhail Naganov13501872023-10-18 16:15:46 -070024#include <audio_utils/clock.h>
Mikhail Naganov6c419352023-11-03 18:33:57 -070025#include <error/Result.h>
Mikhail Naganovcf824f62023-07-24 14:51:36 -070026#include <error/expected_utils.h>
27
Mikhail Naganovcf824f62023-07-24 14:51:36 -070028#include "core-impl/StreamPrimary.h"
Mikhail Naganovcf824f62023-07-24 14:51:36 -070029
30using aidl::android::hardware::audio::common::SinkMetadata;
31using aidl::android::hardware::audio::common::SourceMetadata;
32using aidl::android::media::audio::common::AudioDevice;
Weilin Xu29e51682024-10-02 17:16:32 +000033using aidl::android::media::audio::common::AudioDeviceAddress;
Mikhail Naganovcf824f62023-07-24 14:51:36 -070034using aidl::android::media::audio::common::AudioDeviceDescription;
35using aidl::android::media::audio::common::AudioDeviceType;
36using aidl::android::media::audio::common::AudioOffloadInfo;
37using aidl::android::media::audio::common::MicrophoneInfo;
38using android::base::GetBoolProperty;
39
40namespace aidl::android::hardware::audio::core {
41
Mikhail Naganovf5ec73e2024-10-02 11:02:52 -070042StreamPrimary::StreamPrimary(StreamContext* context, const Metadata& metadata)
Mikhail Naganov13501872023-10-18 16:15:46 -070043 : StreamAlsa(context, metadata, 3 /*readWriteRetries*/),
Weilin Xu29e51682024-10-02 17:16:32 +000044 mIsAsynchronous(!!getContext().getAsyncCallback()),
Mikhail Naganovf5ec73e2024-10-02 11:02:52 -070045 mStubDriver(getContext()) {
Mikhail Naganov3c8b6ce2023-10-31 11:20:30 -070046 context->startStreamDataProcessor();
47}
Mikhail Naganovcf824f62023-07-24 14:51:36 -070048
Mikhail Naganovf5ec73e2024-10-02 11:02:52 -070049::android::status_t StreamPrimary::init() {
50 RETURN_STATUS_IF_ERROR(mStubDriver.init());
51 return StreamAlsa::init();
52}
53
54::android::status_t StreamPrimary::drain(StreamDescriptor::DrainMode mode) {
55 return isStubStreamOnWorker() ? mStubDriver.drain(mode) : StreamAlsa::drain(mode);
56}
57
58::android::status_t StreamPrimary::flush() {
Mikhail Naganov05fc6aa2024-10-11 13:55:54 -070059 RETURN_STATUS_IF_ERROR(isStubStreamOnWorker() ? mStubDriver.flush() : StreamAlsa::flush());
60 // TODO(b/372951987): consider if this needs to be done from 'StreamInWorkerLogic::cycle'.
61 return mIsInput ? standby() : ::android::OK;
Mikhail Naganovf5ec73e2024-10-02 11:02:52 -070062}
63
64::android::status_t StreamPrimary::pause() {
65 return isStubStreamOnWorker() ? mStubDriver.pause() : StreamAlsa::pause();
66}
67
68::android::status_t StreamPrimary::standby() {
69 return isStubStreamOnWorker() ? mStubDriver.standby() : StreamAlsa::standby();
70}
71
Mikhail Naganov6c419352023-11-03 18:33:57 -070072::android::status_t StreamPrimary::start() {
Mikhail Naganovf5ec73e2024-10-02 11:02:52 -070073 bool isStub = true, shutdownAlsaStream = false;
74 {
75 std::lock_guard l(mLock);
76 isStub = mAlsaDeviceId == kStubDeviceId;
77 shutdownAlsaStream =
78 mCurrAlsaDeviceId != mAlsaDeviceId && mCurrAlsaDeviceId != kStubDeviceId;
79 mCurrAlsaDeviceId = mAlsaDeviceId;
80 }
81 if (shutdownAlsaStream) {
82 StreamAlsa::shutdown(); // Close currently opened ALSA devices.
83 }
84 if (isStub) {
85 return mStubDriver.start();
86 }
Mikhail Naganov6c419352023-11-03 18:33:57 -070087 RETURN_STATUS_IF_ERROR(StreamAlsa::start());
88 mStartTimeNs = ::android::uptimeNanos();
89 mFramesSinceStart = 0;
90 mSkipNextTransfer = false;
91 return ::android::OK;
92}
93
Mikhail Naganov13501872023-10-18 16:15:46 -070094::android::status_t StreamPrimary::transfer(void* buffer, size_t frameCount,
95 size_t* actualFrameCount, int32_t* latencyMs) {
Mikhail Naganovf5ec73e2024-10-02 11:02:52 -070096 if (isStubStreamOnWorker()) {
97 return mStubDriver.transfer(buffer, frameCount, actualFrameCount, latencyMs);
98 }
Mikhail Naganov13501872023-10-18 16:15:46 -070099 // This is a workaround for the emulator implementation which has a host-side buffer
Mikhail Naganov6c419352023-11-03 18:33:57 -0700100 // and is not being able to achieve real-time behavior similar to ADSPs (b/302587331).
101 if (!mSkipNextTransfer) {
102 RETURN_STATUS_IF_ERROR(
103 StreamAlsa::transfer(buffer, frameCount, actualFrameCount, latencyMs));
104 } else {
105 LOG(DEBUG) << __func__ << ": skipping transfer (" << frameCount << " frames)";
106 *actualFrameCount = frameCount;
Mikhail Naganovd664a632023-11-27 17:31:04 -0800107 if (mIsInput) memset(buffer, 0, frameCount * mFrameSizeBytes);
Mikhail Naganov6c419352023-11-03 18:33:57 -0700108 mSkipNextTransfer = false;
Mikhail Naganov13501872023-10-18 16:15:46 -0700109 }
Mikhail Naganov6c419352023-11-03 18:33:57 -0700110 if (!mIsAsynchronous) {
111 const long bufferDurationUs =
112 (*actualFrameCount) * MICROS_PER_SECOND / mContext.getSampleRate();
113 const auto totalDurationUs =
114 (::android::uptimeNanos() - mStartTimeNs) / NANOS_PER_MICROSECOND;
115 mFramesSinceStart += *actualFrameCount;
116 const long totalOffsetUs =
117 mFramesSinceStart * MICROS_PER_SECOND / mContext.getSampleRate() - totalDurationUs;
118 LOG(VERBOSE) << __func__ << ": totalOffsetUs " << totalOffsetUs;
119 if (totalOffsetUs > 0) {
120 const long sleepTimeUs = std::min(totalOffsetUs, bufferDurationUs);
121 LOG(VERBOSE) << __func__ << ": sleeping for " << sleepTimeUs << " us";
122 usleep(sleepTimeUs);
123 } else {
124 mSkipNextTransfer = true;
125 }
126 } else {
127 LOG(VERBOSE) << __func__ << ": asynchronous transfer";
128 }
129 return ::android::OK;
130}
131
132::android::status_t StreamPrimary::refinePosition(StreamDescriptor::Position*) {
133 // Since not all data is actually sent to the HAL, use the position maintained by Stream class
134 // which accounts for all frames passed from / to the client.
Mikhail Naganov13501872023-10-18 16:15:46 -0700135 return ::android::OK;
136}
137
Mikhail Naganovf5ec73e2024-10-02 11:02:52 -0700138void StreamPrimary::shutdown() {
139 StreamAlsa::shutdown();
140 mStubDriver.shutdown();
141}
142
143ndk::ScopedAStatus StreamPrimary::setConnectedDevices(const ConnectedDevices& devices) {
144 LOG(DEBUG) << __func__ << ": " << ::android::internal::ToString(devices);
145 if (devices.size() > 1) {
146 LOG(ERROR) << __func__ << ": primary stream can only be connected to one device, got: "
147 << devices.size();
148 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
149 }
150 {
151 const bool useStubDriver = devices.empty() || useStubStream(mIsInput, devices[0]);
152 std::lock_guard l(mLock);
153 mAlsaDeviceId = useStubDriver ? kStubDeviceId : getCardAndDeviceId(devices);
154 }
155 if (!devices.empty()) {
156 auto streamDataProcessor = getContext().getStreamDataProcessor().lock();
157 if (streamDataProcessor != nullptr) {
158 streamDataProcessor->setAudioDevice(devices[0]);
159 }
160 }
161 return StreamAlsa::setConnectedDevices(devices);
162}
163
Mikhail Naganovcf824f62023-07-24 14:51:36 -0700164std::vector<alsa::DeviceProfile> StreamPrimary::getDeviceProfiles() {
Mikhail Naganovf5ec73e2024-10-02 11:02:52 -0700165 return {alsa::DeviceProfile{.card = mCurrAlsaDeviceId.first,
166 .device = mCurrAlsaDeviceId.second,
Weilin Xu29e51682024-10-02 17:16:32 +0000167 .direction = mIsInput ? PCM_IN : PCM_OUT,
Mikhail Naganovcf824f62023-07-24 14:51:36 -0700168 .isExternal = false}};
Weilin Xu29e51682024-10-02 17:16:32 +0000169}
170
Mikhail Naganovf5ec73e2024-10-02 11:02:52 -0700171bool StreamPrimary::isStubStream() {
172 std::lock_guard l(mLock);
173 return mAlsaDeviceId == kStubDeviceId;
174}
175
176// static
177StreamPrimary::AlsaDeviceId StreamPrimary::getCardAndDeviceId(
178 const std::vector<AudioDevice>& devices) {
Weilin Xu29e51682024-10-02 17:16:32 +0000179 if (devices.empty() || devices[0].address.getTag() != AudioDeviceAddress::id) {
180 return kDefaultCardAndDeviceId;
181 }
182 std::string deviceAddress = devices[0].address.get<AudioDeviceAddress::id>();
Mikhail Naganovf5ec73e2024-10-02 11:02:52 -0700183 AlsaDeviceId cardAndDeviceId;
Weilin Xu29e51682024-10-02 17:16:32 +0000184 if (const size_t suffixPos = deviceAddress.rfind("CARD_");
185 suffixPos == std::string::npos ||
186 sscanf(deviceAddress.c_str() + suffixPos, "CARD_%d_DEV_%d", &cardAndDeviceId.first,
187 &cardAndDeviceId.second) != 2) {
188 return kDefaultCardAndDeviceId;
189 }
190 LOG(DEBUG) << __func__ << ": parsed with card id " << cardAndDeviceId.first << ", device id "
191 << cardAndDeviceId.second;
192 return cardAndDeviceId;
Mikhail Naganovcf824f62023-07-24 14:51:36 -0700193}
194
Mikhail Naganovf5ec73e2024-10-02 11:02:52 -0700195// static
196bool StreamPrimary::useStubStream(
197 bool isInput, const ::aidl::android::media::audio::common::AudioDevice& device) {
198 static const bool kSimulateInput =
199 GetBoolProperty("ro.boot.audio.tinyalsa.simulate_input", false);
200 static const bool kSimulateOutput =
201 GetBoolProperty("ro.boot.audio.tinyalsa.ignore_output", false);
202 if (isInput) {
203 return kSimulateInput || device.type.type == AudioDeviceType::IN_TELEPHONY_RX ||
204 device.type.type == AudioDeviceType::IN_FM_TUNER ||
205 device.type.connection == AudioDeviceDescription::CONNECTION_BUS /*deprecated */;
206 }
207 return kSimulateOutput || device.type.type == AudioDeviceType::OUT_TELEPHONY_TX ||
208 device.type.connection == AudioDeviceDescription::CONNECTION_BUS /*deprecated*/;
209}
210
Mikhail Naganovcf824f62023-07-24 14:51:36 -0700211StreamInPrimary::StreamInPrimary(StreamContext&& context, const SinkMetadata& sinkMetadata,
212 const std::vector<MicrophoneInfo>& microphones)
213 : StreamIn(std::move(context), microphones),
Mikhail Naganovf5ec73e2024-10-02 11:02:52 -0700214 StreamPrimary(&mContextInstance, sinkMetadata),
Mikhail Naganovcf824f62023-07-24 14:51:36 -0700215 StreamInHwGainHelper(&mContextInstance) {}
216
Mikhail Naganovcf824f62023-07-24 14:51:36 -0700217ndk::ScopedAStatus StreamInPrimary::getHwGain(std::vector<float>* _aidl_return) {
218 if (isStubStream()) {
219 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
220 }
Jindong Yue5c7e78b2024-10-21 11:18:08 +0800221 if (mHwGains.empty()) {
222 float gain;
223 RETURN_STATUS_IF_ERROR(primary::PrimaryMixer::getInstance().getMicGain(&gain));
224 _aidl_return->resize(mChannelCount, gain);
225 RETURN_STATUS_IF_ERROR(setHwGainImpl(*_aidl_return));
226 }
Mikhail Naganovcf824f62023-07-24 14:51:36 -0700227 return getHwGainImpl(_aidl_return);
228}
229
230ndk::ScopedAStatus StreamInPrimary::setHwGain(const std::vector<float>& in_channelGains) {
231 if (isStubStream()) {
232 LOG(DEBUG) << __func__ << ": gains " << ::android::internal::ToString(in_channelGains);
233 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
234 }
235 auto currentGains = mHwGains;
236 RETURN_STATUS_IF_ERROR(setHwGainImpl(in_channelGains));
237 if (in_channelGains.size() < 1) {
238 LOG(FATAL) << __func__ << ": unexpected gain vector size: " << in_channelGains.size();
239 }
240 if (auto status = primary::PrimaryMixer::getInstance().setMicGain(in_channelGains[0]);
241 !status.isOk()) {
242 mHwGains = currentGains;
243 return status;
244 }
Jindong Yue214c86e2024-10-21 13:42:29 +0800245 float gain;
246 RETURN_STATUS_IF_ERROR(primary::PrimaryMixer::getInstance().getMicGain(&gain));
247 // Due to rounding errors, round trip conversions between percents and indexed values may not
248 // match.
249 if (gain != in_channelGains[0]) {
250 LOG(WARNING) << __func__ << ": unmatched gain: set: " << in_channelGains[0]
251 << ", from mixer: " << gain;
252 }
Mikhail Naganovcf824f62023-07-24 14:51:36 -0700253 return ndk::ScopedAStatus::ok();
254}
255
256StreamOutPrimary::StreamOutPrimary(StreamContext&& context, const SourceMetadata& sourceMetadata,
257 const std::optional<AudioOffloadInfo>& offloadInfo)
258 : StreamOut(std::move(context), offloadInfo),
Mikhail Naganovf5ec73e2024-10-02 11:02:52 -0700259 StreamPrimary(&mContextInstance, sourceMetadata),
Mikhail Naganovcf824f62023-07-24 14:51:36 -0700260 StreamOutHwVolumeHelper(&mContextInstance) {}
261
Mikhail Naganovcf824f62023-07-24 14:51:36 -0700262ndk::ScopedAStatus StreamOutPrimary::getHwVolume(std::vector<float>* _aidl_return) {
263 if (isStubStream()) {
264 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
265 }
Jindong Yue5c7e78b2024-10-21 11:18:08 +0800266 if (mHwVolumes.empty()) {
267 RETURN_STATUS_IF_ERROR(primary::PrimaryMixer::getInstance().getVolumes(_aidl_return));
268 _aidl_return->resize(mChannelCount);
269 RETURN_STATUS_IF_ERROR(setHwVolumeImpl(*_aidl_return));
270 }
Mikhail Naganovcf824f62023-07-24 14:51:36 -0700271 return getHwVolumeImpl(_aidl_return);
272}
273
274ndk::ScopedAStatus StreamOutPrimary::setHwVolume(const std::vector<float>& in_channelVolumes) {
275 if (isStubStream()) {
276 LOG(DEBUG) << __func__ << ": volumes " << ::android::internal::ToString(in_channelVolumes);
277 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
278 }
279 auto currentVolumes = mHwVolumes;
280 RETURN_STATUS_IF_ERROR(setHwVolumeImpl(in_channelVolumes));
281 if (auto status = primary::PrimaryMixer::getInstance().setVolumes(in_channelVolumes);
282 !status.isOk()) {
283 mHwVolumes = currentVolumes;
284 return status;
285 }
Jindong Yue214c86e2024-10-21 13:42:29 +0800286 std::vector<float> volumes;
287 RETURN_STATUS_IF_ERROR(primary::PrimaryMixer::getInstance().getVolumes(&volumes));
288 // Due to rounding errors, round trip conversions between percents and indexed values may not
289 // match.
290 if (volumes != in_channelVolumes) {
291 LOG(WARNING) << __func__ << ": unmatched volumes: set: "
292 << ::android::internal::ToString(in_channelVolumes)
293 << ", from mixer: " << ::android::internal::ToString(volumes);
294 }
Mikhail Naganovcf824f62023-07-24 14:51:36 -0700295 return ndk::ScopedAStatus::ok();
296}
297
Mikhail Naganovcf824f62023-07-24 14:51:36 -0700298} // namespace aidl::android::hardware::audio::core