blob: 1b1ea680ce9bb7aebad831be9d2aba7484cf0e4e [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 }
130 return getHwGainImpl(_aidl_return);
131}
132
133ndk::ScopedAStatus StreamInPrimary::setHwGain(const std::vector<float>& in_channelGains) {
134 if (isStubStream()) {
135 LOG(DEBUG) << __func__ << ": gains " << ::android::internal::ToString(in_channelGains);
136 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
137 }
138 auto currentGains = mHwGains;
139 RETURN_STATUS_IF_ERROR(setHwGainImpl(in_channelGains));
140 if (in_channelGains.size() < 1) {
141 LOG(FATAL) << __func__ << ": unexpected gain vector size: " << in_channelGains.size();
142 }
143 if (auto status = primary::PrimaryMixer::getInstance().setMicGain(in_channelGains[0]);
144 !status.isOk()) {
145 mHwGains = currentGains;
146 return status;
147 }
148 return ndk::ScopedAStatus::ok();
149}
150
151StreamOutPrimary::StreamOutPrimary(StreamContext&& context, const SourceMetadata& sourceMetadata,
152 const std::optional<AudioOffloadInfo>& offloadInfo)
153 : StreamOut(std::move(context), offloadInfo),
154 StreamSwitcher(&mContextInstance, sourceMetadata),
155 StreamOutHwVolumeHelper(&mContextInstance) {}
156
157bool StreamOutPrimary::useStubStream(const AudioDevice& device) {
158 static const bool kSimulateOutput =
159 GetBoolProperty("ro.boot.audio.tinyalsa.ignore_output", false);
160 return kSimulateOutput || device.type.type == AudioDeviceType::OUT_TELEPHONY_TX ||
Mikhail Naganov13501872023-10-18 16:15:46 -0700161 device.type.connection == AudioDeviceDescription::CONNECTION_BUS ||
162 (device.type.type == AudioDeviceType::OUT_DEVICE && device.type.connection.empty());
Mikhail Naganovcf824f62023-07-24 14:51:36 -0700163}
164
165StreamSwitcher::DeviceSwitchBehavior StreamOutPrimary::switchCurrentStream(
166 const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
167 LOG(DEBUG) << __func__;
168 if (devices.size() > 1) {
169 LOG(ERROR) << __func__ << ": primary stream can only be connected to one device, got: "
170 << devices.size();
171 return DeviceSwitchBehavior::UNSUPPORTED_DEVICES;
172 }
173 if (devices.empty() || useStubStream(devices[0]) == isStubStream()) {
174 return DeviceSwitchBehavior::USE_CURRENT_STREAM;
175 }
176 return DeviceSwitchBehavior::CREATE_NEW_STREAM;
177}
178
179std::unique_ptr<StreamCommonInterfaceEx> StreamOutPrimary::createNewStream(
180 const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices,
181 StreamContext* context, const Metadata& metadata) {
182 if (devices.empty()) {
183 LOG(FATAL) << __func__ << ": called with empty devices"; // see 'switchCurrentStream'
184 }
185 if (useStubStream(devices[0])) {
186 return std::unique_ptr<StreamCommonInterfaceEx>(
187 new InnerStreamWrapper<StreamStub>(context, metadata));
188 }
189 return std::unique_ptr<StreamCommonInterfaceEx>(
190 new InnerStreamWrapper<StreamPrimary>(context, metadata));
191}
192
193ndk::ScopedAStatus StreamOutPrimary::getHwVolume(std::vector<float>* _aidl_return) {
194 if (isStubStream()) {
195 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
196 }
197 return getHwVolumeImpl(_aidl_return);
198}
199
200ndk::ScopedAStatus StreamOutPrimary::setHwVolume(const std::vector<float>& in_channelVolumes) {
201 if (isStubStream()) {
202 LOG(DEBUG) << __func__ << ": volumes " << ::android::internal::ToString(in_channelVolumes);
203 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
204 }
205 auto currentVolumes = mHwVolumes;
206 RETURN_STATUS_IF_ERROR(setHwVolumeImpl(in_channelVolumes));
207 if (auto status = primary::PrimaryMixer::getInstance().setVolumes(in_channelVolumes);
208 !status.isOk()) {
209 mHwVolumes = currentVolumes;
210 return status;
211 }
212 return ndk::ScopedAStatus::ok();
213}
214
Mikhail Naganov3c8b6ce2023-10-31 11:20:30 -0700215ndk::ScopedAStatus StreamOutPrimary::setConnectedDevices(
216 const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
217 if (!devices.empty()) {
218 auto streamDataProcessor = mContextInstance.getStreamDataProcessor().lock();
219 if (streamDataProcessor != nullptr) {
220 streamDataProcessor->setAudioDevice(devices[0]);
221 }
222 }
223 return StreamSwitcher::setConnectedDevices(devices);
224}
225
Mikhail Naganovcf824f62023-07-24 14:51:36 -0700226} // namespace aidl::android::hardware::audio::core