Mikhail Naganov | cf824f6 | 2023-07-24 14:51:36 -0700 | [diff] [blame] | 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 | |
Mikhail Naganov | cf824f6 | 2023-07-24 14:51:36 -0700 | [diff] [blame] | 17 | #define LOG_TAG "AHAL_StreamPrimary" |
| 18 | #include <android-base/logging.h> |
| 19 | #include <android-base/properties.h> |
Mikhail Naganov | 1350187 | 2023-10-18 16:15:46 -0700 | [diff] [blame] | 20 | #include <audio_utils/clock.h> |
Mikhail Naganov | 6c41935 | 2023-11-03 18:33:57 -0700 | [diff] [blame] | 21 | #include <error/Result.h> |
Mikhail Naganov | cf824f6 | 2023-07-24 14:51:36 -0700 | [diff] [blame] | 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 | |
| 39 | StreamPrimary::StreamPrimary(StreamContext* context, const Metadata& metadata) |
Mikhail Naganov | 1350187 | 2023-10-18 16:15:46 -0700 | [diff] [blame] | 40 | : StreamAlsa(context, metadata, 3 /*readWriteRetries*/), |
| 41 | mIsAsynchronous(!!getContext().getAsyncCallback()) { |
Mikhail Naganov | 3c8b6ce | 2023-10-31 11:20:30 -0700 | [diff] [blame] | 42 | context->startStreamDataProcessor(); |
| 43 | } |
Mikhail Naganov | cf824f6 | 2023-07-24 14:51:36 -0700 | [diff] [blame] | 44 | |
Mikhail Naganov | 6c41935 | 2023-11-03 18:33:57 -0700 | [diff] [blame] | 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 | |
Mikhail Naganov | 1350187 | 2023-10-18 16:15:46 -0700 | [diff] [blame] | 53 | ::android::status_t StreamPrimary::transfer(void* buffer, size_t frameCount, |
| 54 | size_t* actualFrameCount, int32_t* latencyMs) { |
Mikhail Naganov | 1350187 | 2023-10-18 16:15:46 -0700 | [diff] [blame] | 55 | // This is a workaround for the emulator implementation which has a host-side buffer |
Mikhail Naganov | 6c41935 | 2023-11-03 18:33:57 -0700 | [diff] [blame] | 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; |
Mikhail Naganov | d664a63 | 2023-11-27 17:31:04 -0800 | [diff] [blame^] | 63 | if (mIsInput) memset(buffer, 0, frameCount * mFrameSizeBytes); |
Mikhail Naganov | 6c41935 | 2023-11-03 18:33:57 -0700 | [diff] [blame] | 64 | mSkipNextTransfer = false; |
Mikhail Naganov | 1350187 | 2023-10-18 16:15:46 -0700 | [diff] [blame] | 65 | } |
Mikhail Naganov | 6c41935 | 2023-11-03 18:33:57 -0700 | [diff] [blame] | 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 | |
| 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. |
Mikhail Naganov | 1350187 | 2023-10-18 16:15:46 -0700 | [diff] [blame] | 91 | return ::android::OK; |
| 92 | } |
| 93 | |
Mikhail Naganov | cf824f6 | 2023-07-24 14:51:36 -0700 | [diff] [blame] | 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 | |
| 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 | |
| 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 || |
Mikhail Naganov | 1350187 | 2023-10-18 16:15:46 -0700 | [diff] [blame] | 119 | device.type.connection == AudioDeviceDescription::CONNECTION_BUS || |
| 120 | (device.type.type == AudioDeviceType::IN_DEVICE && device.type.connection.empty()); |
Mikhail Naganov | cf824f6 | 2023-07-24 14:51:36 -0700 | [diff] [blame] | 121 | } |
| 122 | |
| 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 | |
| 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 | |
| 151 | ndk::ScopedAStatus StreamInPrimary::getHwGain(std::vector<float>* _aidl_return) { |
| 152 | if (isStubStream()) { |
| 153 | return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); |
| 154 | } |
Mikhail Naganov | 49bcb92 | 2023-10-30 15:10:51 -0700 | [diff] [blame] | 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)); |
Mikhail Naganov | cf824f6 | 2023-07-24 14:51:36 -0700 | [diff] [blame] | 160 | return getHwGainImpl(_aidl_return); |
| 161 | } |
| 162 | |
| 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 | |
| 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 | |
| 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 || |
Mikhail Naganov | 1350187 | 2023-10-18 16:15:46 -0700 | [diff] [blame] | 191 | device.type.connection == AudioDeviceDescription::CONNECTION_BUS || |
| 192 | (device.type.type == AudioDeviceType::OUT_DEVICE && device.type.connection.empty()); |
Mikhail Naganov | cf824f6 | 2023-07-24 14:51:36 -0700 | [diff] [blame] | 193 | } |
| 194 | |
| 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 | |
| 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 | |
| 223 | ndk::ScopedAStatus StreamOutPrimary::getHwVolume(std::vector<float>* _aidl_return) { |
| 224 | if (isStubStream()) { |
| 225 | return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); |
| 226 | } |
Mikhail Naganov | 49bcb92 | 2023-10-30 15:10:51 -0700 | [diff] [blame] | 227 | RETURN_STATUS_IF_ERROR(primary::PrimaryMixer::getInstance().getVolumes(_aidl_return)); |
| 228 | _aidl_return->resize(mChannelCount); |
| 229 | RETURN_STATUS_IF_ERROR(setHwVolumeImpl(*_aidl_return)); |
Mikhail Naganov | cf824f6 | 2023-07-24 14:51:36 -0700 | [diff] [blame] | 230 | return getHwVolumeImpl(_aidl_return); |
| 231 | } |
| 232 | |
| 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 | |
Mikhail Naganov | 3c8b6ce | 2023-10-31 11:20:30 -0700 | [diff] [blame] | 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 | |
Mikhail Naganov | cf824f6 | 2023-07-24 14:51:36 -0700 | [diff] [blame] | 259 | } // namespace aidl::android::hardware::audio::core |