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