xref: /aosp_15_r20/hardware/interfaces/audio/aidl/default/primary/StreamPrimary.cpp (revision 4d7e907c777eeecc4c5bd7cf640a754fac206ff7)
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