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 #include <android-base/logging.h>
19 #include <android-base/properties.h>
20 #include <audio_utils/clock.h>
21 #include <error/Result.h>
22 #include <error/expected_utils.h>
23
24 #include "PrimaryMixer.h"
25 #include "core-impl/StreamPrimary.h"
26 #include "core-impl/StreamStub.h"
27
28 using aidl::android::hardware::audio::common::SinkMetadata;
29 using aidl::android::hardware::audio::common::SourceMetadata;
30 using aidl::android::media::audio::common::AudioDevice;
31 using aidl::android::media::audio::common::AudioDeviceDescription;
32 using aidl::android::media::audio::common::AudioDeviceType;
33 using aidl::android::media::audio::common::AudioOffloadInfo;
34 using aidl::android::media::audio::common::MicrophoneInfo;
35 using android::base::GetBoolProperty;
36
37 namespace aidl::android::hardware::audio::core {
38
StreamPrimary(StreamContext * context,const Metadata & metadata)39 StreamPrimary::StreamPrimary(StreamContext* context, const Metadata& metadata)
40 : StreamAlsa(context, metadata, 3 /*readWriteRetries*/),
41 mIsAsynchronous(!!getContext().getAsyncCallback()) {
42 context->startStreamDataProcessor();
43 }
44
start()45 ::android::status_t StreamPrimary::start() {
46 RETURN_STATUS_IF_ERROR(StreamAlsa::start());
47 mStartTimeNs = ::android::uptimeNanos();
48 mFramesSinceStart = 0;
49 mSkipNextTransfer = false;
50 return ::android::OK;
51 }
52
transfer(void * buffer,size_t frameCount,size_t * actualFrameCount,int32_t * latencyMs)53 ::android::status_t StreamPrimary::transfer(void* buffer, size_t frameCount,
54 size_t* actualFrameCount, int32_t* latencyMs) {
55 // This is a workaround for the emulator implementation which has a host-side buffer
56 // and is not being able to achieve real-time behavior similar to ADSPs (b/302587331).
57 if (!mSkipNextTransfer) {
58 RETURN_STATUS_IF_ERROR(
59 StreamAlsa::transfer(buffer, frameCount, actualFrameCount, latencyMs));
60 } else {
61 LOG(DEBUG) << __func__ << ": skipping transfer (" << frameCount << " frames)";
62 *actualFrameCount = frameCount;
63 if (mIsInput) memset(buffer, 0, frameCount * mFrameSizeBytes);
64 mSkipNextTransfer = false;
65 }
66 if (!mIsAsynchronous) {
67 const long bufferDurationUs =
68 (*actualFrameCount) * MICROS_PER_SECOND / mContext.getSampleRate();
69 const auto totalDurationUs =
70 (::android::uptimeNanos() - mStartTimeNs) / NANOS_PER_MICROSECOND;
71 mFramesSinceStart += *actualFrameCount;
72 const long totalOffsetUs =
73 mFramesSinceStart * MICROS_PER_SECOND / mContext.getSampleRate() - totalDurationUs;
74 LOG(VERBOSE) << __func__ << ": totalOffsetUs " << totalOffsetUs;
75 if (totalOffsetUs > 0) {
76 const long sleepTimeUs = std::min(totalOffsetUs, bufferDurationUs);
77 LOG(VERBOSE) << __func__ << ": sleeping for " << sleepTimeUs << " us";
78 usleep(sleepTimeUs);
79 } else {
80 mSkipNextTransfer = true;
81 }
82 } else {
83 LOG(VERBOSE) << __func__ << ": asynchronous transfer";
84 }
85 return ::android::OK;
86 }
87
refinePosition(StreamDescriptor::Position *)88 ::android::status_t StreamPrimary::refinePosition(StreamDescriptor::Position*) {
89 // Since not all data is actually sent to the HAL, use the position maintained by Stream class
90 // which accounts for all frames passed from / to the client.
91 return ::android::OK;
92 }
93
getDeviceProfiles()94 std::vector<alsa::DeviceProfile> StreamPrimary::getDeviceProfiles() {
95 static const std::vector<alsa::DeviceProfile> kBuiltInSource{
96 alsa::DeviceProfile{.card = primary::PrimaryMixer::kAlsaCard,
97 .device = primary::PrimaryMixer::kAlsaDevice,
98 .direction = PCM_IN,
99 .isExternal = false}};
100 static const std::vector<alsa::DeviceProfile> kBuiltInSink{
101 alsa::DeviceProfile{.card = primary::PrimaryMixer::kAlsaCard,
102 .device = primary::PrimaryMixer::kAlsaDevice,
103 .direction = PCM_OUT,
104 .isExternal = false}};
105 return mIsInput ? kBuiltInSource : kBuiltInSink;
106 }
107
StreamInPrimary(StreamContext && context,const SinkMetadata & sinkMetadata,const std::vector<MicrophoneInfo> & microphones)108 StreamInPrimary::StreamInPrimary(StreamContext&& context, const SinkMetadata& sinkMetadata,
109 const std::vector<MicrophoneInfo>& microphones)
110 : StreamIn(std::move(context), microphones),
111 StreamSwitcher(&mContextInstance, sinkMetadata),
112 StreamInHwGainHelper(&mContextInstance) {}
113
useStubStream(const AudioDevice & device)114 bool StreamInPrimary::useStubStream(const AudioDevice& device) {
115 static const bool kSimulateInput =
116 GetBoolProperty("ro.boot.audio.tinyalsa.simulate_input", false);
117 return kSimulateInput || device.type.type == AudioDeviceType::IN_TELEPHONY_RX ||
118 device.type.type == AudioDeviceType::IN_FM_TUNER ||
119 device.type.connection == AudioDeviceDescription::CONNECTION_BUS /*deprecated */ ||
120 (device.type.type == AudioDeviceType::IN_BUS && device.type.connection.empty());
121 }
122
switchCurrentStream(const std::vector<::aidl::android::media::audio::common::AudioDevice> & devices)123 StreamSwitcher::DeviceSwitchBehavior StreamInPrimary::switchCurrentStream(
124 const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
125 LOG(DEBUG) << __func__;
126 if (devices.size() > 1) {
127 LOG(ERROR) << __func__ << ": primary stream can only be connected to one device, got: "
128 << devices.size();
129 return DeviceSwitchBehavior::UNSUPPORTED_DEVICES;
130 }
131 if (devices.empty() || useStubStream(devices[0]) == isStubStream()) {
132 return DeviceSwitchBehavior::USE_CURRENT_STREAM;
133 }
134 return DeviceSwitchBehavior::CREATE_NEW_STREAM;
135 }
136
createNewStream(const std::vector<::aidl::android::media::audio::common::AudioDevice> & devices,StreamContext * context,const Metadata & metadata)137 std::unique_ptr<StreamCommonInterfaceEx> StreamInPrimary::createNewStream(
138 const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices,
139 StreamContext* context, const Metadata& metadata) {
140 if (devices.empty()) {
141 LOG(FATAL) << __func__ << ": called with empty devices"; // see 'switchCurrentStream'
142 }
143 if (useStubStream(devices[0])) {
144 return std::unique_ptr<StreamCommonInterfaceEx>(
145 new InnerStreamWrapper<StreamStub>(context, metadata));
146 }
147 return std::unique_ptr<StreamCommonInterfaceEx>(
148 new InnerStreamWrapper<StreamPrimary>(context, metadata));
149 }
150
getHwGain(std::vector<float> * _aidl_return)151 ndk::ScopedAStatus StreamInPrimary::getHwGain(std::vector<float>* _aidl_return) {
152 if (isStubStream()) {
153 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
154 }
155 float gain;
156 RETURN_STATUS_IF_ERROR(primary::PrimaryMixer::getInstance().getMicGain(&gain));
157 _aidl_return->resize(0);
158 _aidl_return->resize(mChannelCount, gain);
159 RETURN_STATUS_IF_ERROR(setHwGainImpl(*_aidl_return));
160 return getHwGainImpl(_aidl_return);
161 }
162
setHwGain(const std::vector<float> & in_channelGains)163 ndk::ScopedAStatus StreamInPrimary::setHwGain(const std::vector<float>& in_channelGains) {
164 if (isStubStream()) {
165 LOG(DEBUG) << __func__ << ": gains " << ::android::internal::ToString(in_channelGains);
166 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
167 }
168 auto currentGains = mHwGains;
169 RETURN_STATUS_IF_ERROR(setHwGainImpl(in_channelGains));
170 if (in_channelGains.size() < 1) {
171 LOG(FATAL) << __func__ << ": unexpected gain vector size: " << in_channelGains.size();
172 }
173 if (auto status = primary::PrimaryMixer::getInstance().setMicGain(in_channelGains[0]);
174 !status.isOk()) {
175 mHwGains = currentGains;
176 return status;
177 }
178 return ndk::ScopedAStatus::ok();
179 }
180
StreamOutPrimary(StreamContext && context,const SourceMetadata & sourceMetadata,const std::optional<AudioOffloadInfo> & offloadInfo)181 StreamOutPrimary::StreamOutPrimary(StreamContext&& context, const SourceMetadata& sourceMetadata,
182 const std::optional<AudioOffloadInfo>& offloadInfo)
183 : StreamOut(std::move(context), offloadInfo),
184 StreamSwitcher(&mContextInstance, sourceMetadata),
185 StreamOutHwVolumeHelper(&mContextInstance) {}
186
useStubStream(const AudioDevice & device)187 bool StreamOutPrimary::useStubStream(const AudioDevice& device) {
188 static const bool kSimulateOutput =
189 GetBoolProperty("ro.boot.audio.tinyalsa.ignore_output", false);
190 return kSimulateOutput || device.type.type == AudioDeviceType::OUT_TELEPHONY_TX ||
191 device.type.connection == AudioDeviceDescription::CONNECTION_BUS /*deprecated*/ ||
192 (device.type.type == AudioDeviceType::OUT_BUS && device.type.connection.empty());
193 }
194
switchCurrentStream(const std::vector<::aidl::android::media::audio::common::AudioDevice> & devices)195 StreamSwitcher::DeviceSwitchBehavior StreamOutPrimary::switchCurrentStream(
196 const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
197 LOG(DEBUG) << __func__;
198 if (devices.size() > 1) {
199 LOG(ERROR) << __func__ << ": primary stream can only be connected to one device, got: "
200 << devices.size();
201 return DeviceSwitchBehavior::UNSUPPORTED_DEVICES;
202 }
203 if (devices.empty() || useStubStream(devices[0]) == isStubStream()) {
204 return DeviceSwitchBehavior::USE_CURRENT_STREAM;
205 }
206 return DeviceSwitchBehavior::CREATE_NEW_STREAM;
207 }
208
createNewStream(const std::vector<::aidl::android::media::audio::common::AudioDevice> & devices,StreamContext * context,const Metadata & metadata)209 std::unique_ptr<StreamCommonInterfaceEx> StreamOutPrimary::createNewStream(
210 const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices,
211 StreamContext* context, const Metadata& metadata) {
212 if (devices.empty()) {
213 LOG(FATAL) << __func__ << ": called with empty devices"; // see 'switchCurrentStream'
214 }
215 if (useStubStream(devices[0])) {
216 return std::unique_ptr<StreamCommonInterfaceEx>(
217 new InnerStreamWrapper<StreamStub>(context, metadata));
218 }
219 return std::unique_ptr<StreamCommonInterfaceEx>(
220 new InnerStreamWrapper<StreamPrimary>(context, metadata));
221 }
222
getHwVolume(std::vector<float> * _aidl_return)223 ndk::ScopedAStatus StreamOutPrimary::getHwVolume(std::vector<float>* _aidl_return) {
224 if (isStubStream()) {
225 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
226 }
227 RETURN_STATUS_IF_ERROR(primary::PrimaryMixer::getInstance().getVolumes(_aidl_return));
228 _aidl_return->resize(mChannelCount);
229 RETURN_STATUS_IF_ERROR(setHwVolumeImpl(*_aidl_return));
230 return getHwVolumeImpl(_aidl_return);
231 }
232
setHwVolume(const std::vector<float> & in_channelVolumes)233 ndk::ScopedAStatus StreamOutPrimary::setHwVolume(const std::vector<float>& in_channelVolumes) {
234 if (isStubStream()) {
235 LOG(DEBUG) << __func__ << ": volumes " << ::android::internal::ToString(in_channelVolumes);
236 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
237 }
238 auto currentVolumes = mHwVolumes;
239 RETURN_STATUS_IF_ERROR(setHwVolumeImpl(in_channelVolumes));
240 if (auto status = primary::PrimaryMixer::getInstance().setVolumes(in_channelVolumes);
241 !status.isOk()) {
242 mHwVolumes = currentVolumes;
243 return status;
244 }
245 return ndk::ScopedAStatus::ok();
246 }
247
setConnectedDevices(const std::vector<::aidl::android::media::audio::common::AudioDevice> & devices)248 ndk::ScopedAStatus StreamOutPrimary::setConnectedDevices(
249 const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
250 if (!devices.empty()) {
251 auto streamDataProcessor = mContextInstance.getStreamDataProcessor().lock();
252 if (streamDataProcessor != nullptr) {
253 streamDataProcessor->setAudioDevice(devices[0]);
254 }
255 }
256 return StreamSwitcher::setConnectedDevices(devices);
257 }
258
259 } // namespace aidl::android::hardware::audio::core
260