• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <limits>
18 
19 #define LOG_TAG "AHAL_StreamPrimary"
20 #include <android-base/logging.h>
21 #include <android-base/properties.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*/), mIsInput(isInput(metadata)) {}
41 
getDeviceProfiles()42 std::vector<alsa::DeviceProfile> StreamPrimary::getDeviceProfiles() {
43     static const std::vector<alsa::DeviceProfile> kBuiltInSource{
44             alsa::DeviceProfile{.card = primary::PrimaryMixer::kAlsaCard,
45                                 .device = primary::PrimaryMixer::kAlsaDevice,
46                                 .direction = PCM_IN,
47                                 .isExternal = false}};
48     static const std::vector<alsa::DeviceProfile> kBuiltInSink{
49             alsa::DeviceProfile{.card = primary::PrimaryMixer::kAlsaCard,
50                                 .device = primary::PrimaryMixer::kAlsaDevice,
51                                 .direction = PCM_OUT,
52                                 .isExternal = false}};
53     return mIsInput ? kBuiltInSource : kBuiltInSink;
54 }
55 
StreamInPrimary(StreamContext && context,const SinkMetadata & sinkMetadata,const std::vector<MicrophoneInfo> & microphones)56 StreamInPrimary::StreamInPrimary(StreamContext&& context, const SinkMetadata& sinkMetadata,
57                                  const std::vector<MicrophoneInfo>& microphones)
58     : StreamIn(std::move(context), microphones),
59       StreamSwitcher(&mContextInstance, sinkMetadata),
60       StreamInHwGainHelper(&mContextInstance) {}
61 
useStubStream(const AudioDevice & device)62 bool StreamInPrimary::useStubStream(const AudioDevice& device) {
63     static const bool kSimulateInput =
64             GetBoolProperty("ro.boot.audio.tinyalsa.simulate_input", false);
65     return kSimulateInput || device.type.type == AudioDeviceType::IN_TELEPHONY_RX ||
66            device.type.type == AudioDeviceType::IN_FM_TUNER ||
67            device.type.connection == AudioDeviceDescription::CONNECTION_BUS;
68 }
69 
switchCurrentStream(const std::vector<::aidl::android::media::audio::common::AudioDevice> & devices)70 StreamSwitcher::DeviceSwitchBehavior StreamInPrimary::switchCurrentStream(
71         const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
72     LOG(DEBUG) << __func__;
73     if (devices.size() > 1) {
74         LOG(ERROR) << __func__ << ": primary stream can only be connected to one device, got: "
75                    << devices.size();
76         return DeviceSwitchBehavior::UNSUPPORTED_DEVICES;
77     }
78     if (devices.empty() || useStubStream(devices[0]) == isStubStream()) {
79         return DeviceSwitchBehavior::USE_CURRENT_STREAM;
80     }
81     return DeviceSwitchBehavior::CREATE_NEW_STREAM;
82 }
83 
createNewStream(const std::vector<::aidl::android::media::audio::common::AudioDevice> & devices,StreamContext * context,const Metadata & metadata)84 std::unique_ptr<StreamCommonInterfaceEx> StreamInPrimary::createNewStream(
85         const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices,
86         StreamContext* context, const Metadata& metadata) {
87     if (devices.empty()) {
88         LOG(FATAL) << __func__ << ": called with empty devices";  // see 'switchCurrentStream'
89     }
90     if (useStubStream(devices[0])) {
91         return std::unique_ptr<StreamCommonInterfaceEx>(
92                 new InnerStreamWrapper<StreamStub>(context, metadata));
93     }
94     return std::unique_ptr<StreamCommonInterfaceEx>(
95             new InnerStreamWrapper<StreamPrimary>(context, metadata));
96 }
97 
getHwGain(std::vector<float> * _aidl_return)98 ndk::ScopedAStatus StreamInPrimary::getHwGain(std::vector<float>* _aidl_return) {
99     if (isStubStream()) {
100         return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
101     }
102     return getHwGainImpl(_aidl_return);
103 }
104 
setHwGain(const std::vector<float> & in_channelGains)105 ndk::ScopedAStatus StreamInPrimary::setHwGain(const std::vector<float>& in_channelGains) {
106     if (isStubStream()) {
107         LOG(DEBUG) << __func__ << ": gains " << ::android::internal::ToString(in_channelGains);
108         return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
109     }
110     auto currentGains = mHwGains;
111     RETURN_STATUS_IF_ERROR(setHwGainImpl(in_channelGains));
112     if (in_channelGains.size() < 1) {
113         LOG(FATAL) << __func__ << ": unexpected gain vector size: " << in_channelGains.size();
114     }
115     if (auto status = primary::PrimaryMixer::getInstance().setMicGain(in_channelGains[0]);
116         !status.isOk()) {
117         mHwGains = currentGains;
118         return status;
119     }
120     return ndk::ScopedAStatus::ok();
121 }
122 
StreamOutPrimary(StreamContext && context,const SourceMetadata & sourceMetadata,const std::optional<AudioOffloadInfo> & offloadInfo)123 StreamOutPrimary::StreamOutPrimary(StreamContext&& context, const SourceMetadata& sourceMetadata,
124                                    const std::optional<AudioOffloadInfo>& offloadInfo)
125     : StreamOut(std::move(context), offloadInfo),
126       StreamSwitcher(&mContextInstance, sourceMetadata),
127       StreamOutHwVolumeHelper(&mContextInstance) {}
128 
useStubStream(const AudioDevice & device)129 bool StreamOutPrimary::useStubStream(const AudioDevice& device) {
130     static const bool kSimulateOutput =
131             GetBoolProperty("ro.boot.audio.tinyalsa.ignore_output", false);
132     return kSimulateOutput || device.type.type == AudioDeviceType::OUT_TELEPHONY_TX ||
133            device.type.connection == AudioDeviceDescription::CONNECTION_BUS;
134 }
135 
switchCurrentStream(const std::vector<::aidl::android::media::audio::common::AudioDevice> & devices)136 StreamSwitcher::DeviceSwitchBehavior StreamOutPrimary::switchCurrentStream(
137         const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
138     LOG(DEBUG) << __func__;
139     if (devices.size() > 1) {
140         LOG(ERROR) << __func__ << ": primary stream can only be connected to one device, got: "
141                    << devices.size();
142         return DeviceSwitchBehavior::UNSUPPORTED_DEVICES;
143     }
144     if (devices.empty() || useStubStream(devices[0]) == isStubStream()) {
145         return DeviceSwitchBehavior::USE_CURRENT_STREAM;
146     }
147     return DeviceSwitchBehavior::CREATE_NEW_STREAM;
148 }
149 
createNewStream(const std::vector<::aidl::android::media::audio::common::AudioDevice> & devices,StreamContext * context,const Metadata & metadata)150 std::unique_ptr<StreamCommonInterfaceEx> StreamOutPrimary::createNewStream(
151         const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices,
152         StreamContext* context, const Metadata& metadata) {
153     if (devices.empty()) {
154         LOG(FATAL) << __func__ << ": called with empty devices";  // see 'switchCurrentStream'
155     }
156     if (useStubStream(devices[0])) {
157         return std::unique_ptr<StreamCommonInterfaceEx>(
158                 new InnerStreamWrapper<StreamStub>(context, metadata));
159     }
160     return std::unique_ptr<StreamCommonInterfaceEx>(
161             new InnerStreamWrapper<StreamPrimary>(context, metadata));
162 }
163 
getHwVolume(std::vector<float> * _aidl_return)164 ndk::ScopedAStatus StreamOutPrimary::getHwVolume(std::vector<float>* _aidl_return) {
165     if (isStubStream()) {
166         return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
167     }
168     return getHwVolumeImpl(_aidl_return);
169 }
170 
setHwVolume(const std::vector<float> & in_channelVolumes)171 ndk::ScopedAStatus StreamOutPrimary::setHwVolume(const std::vector<float>& in_channelVolumes) {
172     if (isStubStream()) {
173         LOG(DEBUG) << __func__ << ": volumes " << ::android::internal::ToString(in_channelVolumes);
174         return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
175     }
176     auto currentVolumes = mHwVolumes;
177     RETURN_STATUS_IF_ERROR(setHwVolumeImpl(in_channelVolumes));
178     if (auto status = primary::PrimaryMixer::getInstance().setVolumes(in_channelVolumes);
179         !status.isOk()) {
180         mHwVolumes = currentVolumes;
181         return status;
182     }
183     return ndk::ScopedAStatus::ok();
184 }
185 
186 }  // namespace aidl::android::hardware::audio::core
187