1 /*
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
17 #define LOG_TAG "AHAL_StreamPrimary"
18
19 #include <cstdio>
20
21 #include <android-base/logging.h>
22 #include <android-base/parseint.h>
23 #include <android-base/properties.h>
24 #include <audio_utils/clock.h>
25 #include <error/Result.h>
26 #include <error/expected_utils.h>
27
28 #include "core-impl/StreamPrimary.h"
29
30 using aidl::android::hardware::audio::common::SinkMetadata;
31 using aidl::android::hardware::audio::common::SourceMetadata;
32 using aidl::android::media::audio::common::AudioDevice;
33 using aidl::android::media::audio::common::AudioDeviceAddress;
34 using aidl::android::media::audio::common::AudioDeviceDescription;
35 using aidl::android::media::audio::common::AudioDeviceType;
36 using aidl::android::media::audio::common::AudioOffloadInfo;
37 using aidl::android::media::audio::common::MicrophoneInfo;
38 using android::base::GetBoolProperty;
39
40 namespace aidl::android::hardware::audio::core {
41
StreamPrimary(StreamContext * context,const Metadata & metadata)42 StreamPrimary::StreamPrimary(StreamContext* context, const Metadata& metadata)
43 : StreamAlsa(context, metadata, 3 /*readWriteRetries*/),
44 mIsAsynchronous(!!getContext().getAsyncCallback()),
45 mStubDriver(getContext()) {
46 context->startStreamDataProcessor();
47 }
48
init()49 ::android::status_t StreamPrimary::init() {
50 RETURN_STATUS_IF_ERROR(mStubDriver.init());
51 return StreamAlsa::init();
52 }
53
drain(StreamDescriptor::DrainMode mode)54 ::android::status_t StreamPrimary::drain(StreamDescriptor::DrainMode mode) {
55 return isStubStreamOnWorker() ? mStubDriver.drain(mode) : StreamAlsa::drain(mode);
56 }
57
flush()58 ::android::status_t StreamPrimary::flush() {
59 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;
62 }
63
pause()64 ::android::status_t StreamPrimary::pause() {
65 return isStubStreamOnWorker() ? mStubDriver.pause() : StreamAlsa::pause();
66 }
67
standby()68 ::android::status_t StreamPrimary::standby() {
69 return isStubStreamOnWorker() ? mStubDriver.standby() : StreamAlsa::standby();
70 }
71
start()72 ::android::status_t StreamPrimary::start() {
73 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 }
87 RETURN_STATUS_IF_ERROR(StreamAlsa::start());
88 mStartTimeNs = ::android::uptimeNanos();
89 mFramesSinceStart = 0;
90 mSkipNextTransfer = false;
91 return ::android::OK;
92 }
93
transfer(void * buffer,size_t frameCount,size_t * actualFrameCount,int32_t * latencyMs)94 ::android::status_t StreamPrimary::transfer(void* buffer, size_t frameCount,
95 size_t* actualFrameCount, int32_t* latencyMs) {
96 if (isStubStreamOnWorker()) {
97 return mStubDriver.transfer(buffer, frameCount, actualFrameCount, latencyMs);
98 }
99 // This is a workaround for the emulator implementation which has a host-side buffer
100 // 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;
107 if (mIsInput) memset(buffer, 0, frameCount * mFrameSizeBytes);
108 mSkipNextTransfer = false;
109 }
110 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
refinePosition(StreamDescriptor::Position *)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.
135 return ::android::OK;
136 }
137
shutdown()138 void StreamPrimary::shutdown() {
139 StreamAlsa::shutdown();
140 mStubDriver.shutdown();
141 }
142
setConnectedDevices(const ConnectedDevices & devices)143 ndk::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
getDeviceProfiles()164 std::vector<alsa::DeviceProfile> StreamPrimary::getDeviceProfiles() {
165 return {alsa::DeviceProfile{.card = mCurrAlsaDeviceId.first,
166 .device = mCurrAlsaDeviceId.second,
167 .direction = mIsInput ? PCM_IN : PCM_OUT,
168 .isExternal = false}};
169 }
170
isStubStream()171 bool StreamPrimary::isStubStream() {
172 std::lock_guard l(mLock);
173 return mAlsaDeviceId == kStubDeviceId;
174 }
175
176 // static
getCardAndDeviceId(const std::vector<AudioDevice> & devices)177 StreamPrimary::AlsaDeviceId StreamPrimary::getCardAndDeviceId(
178 const std::vector<AudioDevice>& devices) {
179 if (devices.empty() || devices[0].address.getTag() != AudioDeviceAddress::id) {
180 return kDefaultCardAndDeviceId;
181 }
182 std::string deviceAddress = devices[0].address.get<AudioDeviceAddress::id>();
183 AlsaDeviceId cardAndDeviceId;
184 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;
193 }
194
195 // static
useStubStream(bool isInput,const::aidl::android::media::audio::common::AudioDevice & device)196 bool 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
StreamInPrimary(StreamContext && context,const SinkMetadata & sinkMetadata,const std::vector<MicrophoneInfo> & microphones)211 StreamInPrimary::StreamInPrimary(StreamContext&& context, const SinkMetadata& sinkMetadata,
212 const std::vector<MicrophoneInfo>& microphones)
213 : StreamIn(std::move(context), microphones),
214 StreamPrimary(&mContextInstance, sinkMetadata),
215 StreamInHwGainHelper(&mContextInstance) {}
216
getHwGain(std::vector<float> * _aidl_return)217 ndk::ScopedAStatus StreamInPrimary::getHwGain(std::vector<float>* _aidl_return) {
218 if (isStubStream()) {
219 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
220 }
221 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 }
227 return getHwGainImpl(_aidl_return);
228 }
229
setHwGain(const std::vector<float> & in_channelGains)230 ndk::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 }
245 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 }
253 return ndk::ScopedAStatus::ok();
254 }
255
StreamOutPrimary(StreamContext && context,const SourceMetadata & sourceMetadata,const std::optional<AudioOffloadInfo> & offloadInfo)256 StreamOutPrimary::StreamOutPrimary(StreamContext&& context, const SourceMetadata& sourceMetadata,
257 const std::optional<AudioOffloadInfo>& offloadInfo)
258 : StreamOut(std::move(context), offloadInfo),
259 StreamPrimary(&mContextInstance, sourceMetadata),
260 StreamOutHwVolumeHelper(&mContextInstance) {}
261
getHwVolume(std::vector<float> * _aidl_return)262 ndk::ScopedAStatus StreamOutPrimary::getHwVolume(std::vector<float>* _aidl_return) {
263 if (isStubStream()) {
264 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
265 }
266 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 }
271 return getHwVolumeImpl(_aidl_return);
272 }
273
setHwVolume(const std::vector<float> & in_channelVolumes)274 ndk::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 }
286 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 }
295 return ndk::ScopedAStatus::ok();
296 }
297
298 } // namespace aidl::android::hardware::audio::core
299