blob: 7e3bdd468b6f00b4f1ed6ae5f14ced896b825ff1 [file] [log] [blame]
Mikhail Naganovcf824f62023-07-24 14:51:36 -07001/*
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 Naganov13501872023-10-18 16:15:46 -070017#include <chrono>
Mikhail Naganovcf824f62023-07-24 14:51:36 -070018
19#define LOG_TAG "AHAL_StreamPrimary"
20#include <android-base/logging.h>
21#include <android-base/properties.h>
Mikhail Naganov13501872023-10-18 16:15:46 -070022#include <audio_utils/clock.h>
Mikhail Naganovcf824f62023-07-24 14:51:36 -070023#include <error/expected_utils.h>
24
25#include "PrimaryMixer.h"
26#include "core-impl/StreamPrimary.h"
27#include "core-impl/StreamStub.h"
28
29using aidl::android::hardware::audio::common::SinkMetadata;
30using aidl::android::hardware::audio::common::SourceMetadata;
31using aidl::android::media::audio::common::AudioDevice;
32using aidl::android::media::audio::common::AudioDeviceDescription;
33using aidl::android::media::audio::common::AudioDeviceType;
34using aidl::android::media::audio::common::AudioOffloadInfo;
35using aidl::android::media::audio::common::MicrophoneInfo;
36using android::base::GetBoolProperty;
37
38namespace aidl::android::hardware::audio::core {
39
40StreamPrimary::StreamPrimary(StreamContext* context, const Metadata& metadata)
Mikhail Naganov13501872023-10-18 16:15:46 -070041 : StreamAlsa(context, metadata, 3 /*readWriteRetries*/),
42 mIsAsynchronous(!!getContext().getAsyncCallback()) {
Mikhail Naganov3c8b6ce2023-10-31 11:20:30 -070043 context->startStreamDataProcessor();
44}
Mikhail Naganovcf824f62023-07-24 14:51:36 -070045
Mikhail Naganov13501872023-10-18 16:15:46 -070046::android::status_t StreamPrimary::transfer(void* buffer, size_t frameCount,
47 size_t* actualFrameCount, int32_t* latencyMs) {
48 auto start = std::chrono::steady_clock::now();
49 if (auto status = StreamAlsa::transfer(buffer, frameCount, actualFrameCount, latencyMs);
50 status != ::android::OK) {
51 return status;
52 }
53 // This is a workaround for the emulator implementation which has a host-side buffer
54 // and this can result in reading faster than real time.
55 if (mIsInput && !mIsAsynchronous) {
56 auto recordDurationUs = std::chrono::duration_cast<std::chrono::microseconds>(
57 std::chrono::steady_clock::now() - start);
58 const long projectedVsObservedOffsetUs =
59 *actualFrameCount * MICROS_PER_SECOND / mContext.getSampleRate() -
60 recordDurationUs.count();
61 if (projectedVsObservedOffsetUs > 0) {
62 LOG(VERBOSE) << __func__ << ": sleeping for " << projectedVsObservedOffsetUs << " us";
63 usleep(projectedVsObservedOffsetUs);
64 }
65 }
66 return ::android::OK;
67}
68
Mikhail Naganovcf824f62023-07-24 14:51:36 -070069std::vector<alsa::DeviceProfile> StreamPrimary::getDeviceProfiles() {
70 static const std::vector<alsa::DeviceProfile> kBuiltInSource{
71 alsa::DeviceProfile{.card = primary::PrimaryMixer::kAlsaCard,
72 .device = primary::PrimaryMixer::kAlsaDevice,
73 .direction = PCM_IN,
74 .isExternal = false}};
75 static const std::vector<alsa::DeviceProfile> kBuiltInSink{
76 alsa::DeviceProfile{.card = primary::PrimaryMixer::kAlsaCard,
77 .device = primary::PrimaryMixer::kAlsaDevice,
78 .direction = PCM_OUT,
79 .isExternal = false}};
80 return mIsInput ? kBuiltInSource : kBuiltInSink;
81}
82
83StreamInPrimary::StreamInPrimary(StreamContext&& context, const SinkMetadata& sinkMetadata,
84 const std::vector<MicrophoneInfo>& microphones)
85 : StreamIn(std::move(context), microphones),
86 StreamSwitcher(&mContextInstance, sinkMetadata),
87 StreamInHwGainHelper(&mContextInstance) {}
88
89bool StreamInPrimary::useStubStream(const AudioDevice& device) {
90 static const bool kSimulateInput =
91 GetBoolProperty("ro.boot.audio.tinyalsa.simulate_input", false);
92 return kSimulateInput || device.type.type == AudioDeviceType::IN_TELEPHONY_RX ||
93 device.type.type == AudioDeviceType::IN_FM_TUNER ||
Mikhail Naganov13501872023-10-18 16:15:46 -070094 device.type.connection == AudioDeviceDescription::CONNECTION_BUS ||
95 (device.type.type == AudioDeviceType::IN_DEVICE && device.type.connection.empty());
Mikhail Naganovcf824f62023-07-24 14:51:36 -070096}
97
98StreamSwitcher::DeviceSwitchBehavior StreamInPrimary::switchCurrentStream(
99 const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
100 LOG(DEBUG) << __func__;
101 if (devices.size() > 1) {
102 LOG(ERROR) << __func__ << ": primary stream can only be connected to one device, got: "
103 << devices.size();
104 return DeviceSwitchBehavior::UNSUPPORTED_DEVICES;
105 }
106 if (devices.empty() || useStubStream(devices[0]) == isStubStream()) {
107 return DeviceSwitchBehavior::USE_CURRENT_STREAM;
108 }
109 return DeviceSwitchBehavior::CREATE_NEW_STREAM;
110}
111
112std::unique_ptr<StreamCommonInterfaceEx> StreamInPrimary::createNewStream(
113 const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices,
114 StreamContext* context, const Metadata& metadata) {
115 if (devices.empty()) {
116 LOG(FATAL) << __func__ << ": called with empty devices"; // see 'switchCurrentStream'
117 }
118 if (useStubStream(devices[0])) {
119 return std::unique_ptr<StreamCommonInterfaceEx>(
120 new InnerStreamWrapper<StreamStub>(context, metadata));
121 }
122 return std::unique_ptr<StreamCommonInterfaceEx>(
123 new InnerStreamWrapper<StreamPrimary>(context, metadata));
124}
125
126ndk::ScopedAStatus StreamInPrimary::getHwGain(std::vector<float>* _aidl_return) {
127 if (isStubStream()) {
128 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
129 }
Mikhail Naganov49bcb922023-10-30 15:10:51 -0700130 float gain;
131 RETURN_STATUS_IF_ERROR(primary::PrimaryMixer::getInstance().getMicGain(&gain));
132 _aidl_return->resize(0);
133 _aidl_return->resize(mChannelCount, gain);
134 RETURN_STATUS_IF_ERROR(setHwGainImpl(*_aidl_return));
Mikhail Naganovcf824f62023-07-24 14:51:36 -0700135 return getHwGainImpl(_aidl_return);
136}
137
138ndk::ScopedAStatus StreamInPrimary::setHwGain(const std::vector<float>& in_channelGains) {
139 if (isStubStream()) {
140 LOG(DEBUG) << __func__ << ": gains " << ::android::internal::ToString(in_channelGains);
141 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
142 }
143 auto currentGains = mHwGains;
144 RETURN_STATUS_IF_ERROR(setHwGainImpl(in_channelGains));
145 if (in_channelGains.size() < 1) {
146 LOG(FATAL) << __func__ << ": unexpected gain vector size: " << in_channelGains.size();
147 }
148 if (auto status = primary::PrimaryMixer::getInstance().setMicGain(in_channelGains[0]);
149 !status.isOk()) {
150 mHwGains = currentGains;
151 return status;
152 }
153 return ndk::ScopedAStatus::ok();
154}
155
156StreamOutPrimary::StreamOutPrimary(StreamContext&& context, const SourceMetadata& sourceMetadata,
157 const std::optional<AudioOffloadInfo>& offloadInfo)
158 : StreamOut(std::move(context), offloadInfo),
159 StreamSwitcher(&mContextInstance, sourceMetadata),
160 StreamOutHwVolumeHelper(&mContextInstance) {}
161
162bool StreamOutPrimary::useStubStream(const AudioDevice& device) {
163 static const bool kSimulateOutput =
164 GetBoolProperty("ro.boot.audio.tinyalsa.ignore_output", false);
165 return kSimulateOutput || device.type.type == AudioDeviceType::OUT_TELEPHONY_TX ||
Mikhail Naganov13501872023-10-18 16:15:46 -0700166 device.type.connection == AudioDeviceDescription::CONNECTION_BUS ||
167 (device.type.type == AudioDeviceType::OUT_DEVICE && device.type.connection.empty());
Mikhail Naganovcf824f62023-07-24 14:51:36 -0700168}
169
170StreamSwitcher::DeviceSwitchBehavior StreamOutPrimary::switchCurrentStream(
171 const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
172 LOG(DEBUG) << __func__;
173 if (devices.size() > 1) {
174 LOG(ERROR) << __func__ << ": primary stream can only be connected to one device, got: "
175 << devices.size();
176 return DeviceSwitchBehavior::UNSUPPORTED_DEVICES;
177 }
178 if (devices.empty() || useStubStream(devices[0]) == isStubStream()) {
179 return DeviceSwitchBehavior::USE_CURRENT_STREAM;
180 }
181 return DeviceSwitchBehavior::CREATE_NEW_STREAM;
182}
183
184std::unique_ptr<StreamCommonInterfaceEx> StreamOutPrimary::createNewStream(
185 const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices,
186 StreamContext* context, const Metadata& metadata) {
187 if (devices.empty()) {
188 LOG(FATAL) << __func__ << ": called with empty devices"; // see 'switchCurrentStream'
189 }
190 if (useStubStream(devices[0])) {
191 return std::unique_ptr<StreamCommonInterfaceEx>(
192 new InnerStreamWrapper<StreamStub>(context, metadata));
193 }
194 return std::unique_ptr<StreamCommonInterfaceEx>(
195 new InnerStreamWrapper<StreamPrimary>(context, metadata));
196}
197
198ndk::ScopedAStatus StreamOutPrimary::getHwVolume(std::vector<float>* _aidl_return) {
199 if (isStubStream()) {
200 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
201 }
Mikhail Naganov49bcb922023-10-30 15:10:51 -0700202 RETURN_STATUS_IF_ERROR(primary::PrimaryMixer::getInstance().getVolumes(_aidl_return));
203 _aidl_return->resize(mChannelCount);
204 RETURN_STATUS_IF_ERROR(setHwVolumeImpl(*_aidl_return));
Mikhail Naganovcf824f62023-07-24 14:51:36 -0700205 return getHwVolumeImpl(_aidl_return);
206}
207
208ndk::ScopedAStatus StreamOutPrimary::setHwVolume(const std::vector<float>& in_channelVolumes) {
209 if (isStubStream()) {
210 LOG(DEBUG) << __func__ << ": volumes " << ::android::internal::ToString(in_channelVolumes);
211 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
212 }
213 auto currentVolumes = mHwVolumes;
214 RETURN_STATUS_IF_ERROR(setHwVolumeImpl(in_channelVolumes));
215 if (auto status = primary::PrimaryMixer::getInstance().setVolumes(in_channelVolumes);
216 !status.isOk()) {
217 mHwVolumes = currentVolumes;
218 return status;
219 }
220 return ndk::ScopedAStatus::ok();
221}
222
Mikhail Naganov3c8b6ce2023-10-31 11:20:30 -0700223ndk::ScopedAStatus StreamOutPrimary::setConnectedDevices(
224 const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
225 if (!devices.empty()) {
226 auto streamDataProcessor = mContextInstance.getStreamDataProcessor().lock();
227 if (streamDataProcessor != nullptr) {
228 streamDataProcessor->setAudioDevice(devices[0]);
229 }
230 }
231 return StreamSwitcher::setConnectedDevices(devices);
232}
233
Mikhail Naganovcf824f62023-07-24 14:51:36 -0700234} // namespace aidl::android::hardware::audio::core