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_StreamSwitcher"
20
21 #include <Utils.h>
22 #include <android-base/logging.h>
23 #include <error/expected_utils.h>
24
25 #include "core-impl/StreamStub.h"
26 #include "core-impl/StreamSwitcher.h"
27
28 using aidl::android::hardware::audio::effect::IEffect;
29 using aidl::android::media::audio::common::AudioDevice;
30
31 namespace aidl::android::hardware::audio::core {
32
StreamSwitcher(StreamContext * context,const Metadata & metadata)33 StreamSwitcher::StreamSwitcher(StreamContext* context, const Metadata& metadata)
34 : mContext(context),
35 mMetadata(metadata),
36 mStream(new InnerStreamWrapper<StreamStub>(context, mMetadata)) {}
37
closeCurrentStream(bool validateStreamState)38 ndk::ScopedAStatus StreamSwitcher::closeCurrentStream(bool validateStreamState) {
39 if (!mStream) return ndk::ScopedAStatus::ok();
40 RETURN_STATUS_IF_ERROR(mStream->prepareToClose());
41 RETURN_STATUS_IF_ERROR(mStream->close());
42 if (validateStreamState && !isValidClosingStreamState(mStream->getStatePriorToClosing())) {
43 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
44 }
45 mStream.reset();
46 return ndk::ScopedAStatus::ok();
47 }
48
close()49 ndk::ScopedAStatus StreamSwitcher::close() {
50 if (mStream != nullptr) {
51 auto status = closeCurrentStream(false /*validateStreamState*/);
52 // The actual state is irrelevant since only StreamSwitcher cares about it.
53 onClose(StreamDescriptor::State::STANDBY);
54 return status;
55 }
56 LOG(ERROR) << __func__ << ": stream was already closed";
57 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
58 }
59
prepareToClose()60 ndk::ScopedAStatus StreamSwitcher::prepareToClose() {
61 if (mStream != nullptr) {
62 return mStream->prepareToClose();
63 }
64 LOG(ERROR) << __func__ << ": stream was closed";
65 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
66 }
67
updateHwAvSyncId(int32_t in_hwAvSyncId)68 ndk::ScopedAStatus StreamSwitcher::updateHwAvSyncId(int32_t in_hwAvSyncId) {
69 if (mStream == nullptr) {
70 LOG(ERROR) << __func__ << ": stream was closed";
71 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
72 }
73 RETURN_STATUS_IF_ERROR(mStream->updateHwAvSyncId(in_hwAvSyncId));
74 mHwAvSyncId = in_hwAvSyncId;
75 return ndk::ScopedAStatus::ok();
76 }
77
getVendorParameters(const std::vector<std::string> & in_ids,std::vector<VendorParameter> * _aidl_return)78 ndk::ScopedAStatus StreamSwitcher::getVendorParameters(const std::vector<std::string>& in_ids,
79 std::vector<VendorParameter>* _aidl_return) {
80 if (mStream == nullptr) {
81 LOG(ERROR) << __func__ << ": stream was closed";
82 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
83 }
84 if (mIsStubStream) {
85 LOG(ERROR) << __func__ << ": the stream is not connected";
86 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
87 }
88 return mStream->getVendorParameters(in_ids, _aidl_return);
89 }
90
setVendorParameters(const std::vector<VendorParameter> & in_parameters,bool in_async)91 ndk::ScopedAStatus StreamSwitcher::setVendorParameters(
92 const std::vector<VendorParameter>& in_parameters, bool in_async) {
93 if (mStream == nullptr) {
94 LOG(ERROR) << __func__ << ": stream was closed";
95 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
96 }
97 if (mIsStubStream) {
98 mMissedParameters.emplace_back(in_parameters, in_async);
99 return ndk::ScopedAStatus::ok();
100 }
101 return mStream->setVendorParameters(in_parameters, in_async);
102 }
103
addEffect(const std::shared_ptr<IEffect> & in_effect)104 ndk::ScopedAStatus StreamSwitcher::addEffect(const std::shared_ptr<IEffect>& in_effect) {
105 if (in_effect == nullptr) {
106 LOG(DEBUG) << __func__ << ": null effect";
107 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
108 }
109 if (mStream == nullptr) {
110 LOG(ERROR) << __func__ << ": stream was closed";
111 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
112 }
113 if (!mIsStubStream) {
114 RETURN_STATUS_IF_ERROR(mStream->addEffect(in_effect));
115 }
116 mEffects.push_back(in_effect);
117 return ndk::ScopedAStatus::ok();
118 }
119
removeEffect(const std::shared_ptr<IEffect> & in_effect)120 ndk::ScopedAStatus StreamSwitcher::removeEffect(const std::shared_ptr<IEffect>& in_effect) {
121 if (in_effect == nullptr) {
122 LOG(DEBUG) << __func__ << ": null effect";
123 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
124 }
125 if (mStream == nullptr) {
126 LOG(ERROR) << __func__ << ": stream was closed";
127 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
128 }
129 for (auto it = mEffects.begin(); it != mEffects.end(); ++it) {
130 if ((*it)->asBinder() == in_effect->asBinder()) {
131 mEffects.erase(it);
132 break;
133 }
134 }
135 return !mIsStubStream ? mStream->removeEffect(in_effect) : ndk::ScopedAStatus::ok();
136 }
137
getStreamCommonCommon(std::shared_ptr<IStreamCommon> * _aidl_return)138 ndk::ScopedAStatus StreamSwitcher::getStreamCommonCommon(
139 std::shared_ptr<IStreamCommon>* _aidl_return) {
140 if (!mCommon) {
141 LOG(FATAL) << __func__ << ": the common interface was not created";
142 }
143 *_aidl_return = mCommon.getInstance();
144 LOG(DEBUG) << __func__ << ": returning " << _aidl_return->get()->asBinder().get();
145 return ndk::ScopedAStatus::ok();
146 }
147
updateMetadataCommon(const Metadata & metadata)148 ndk::ScopedAStatus StreamSwitcher::updateMetadataCommon(const Metadata& metadata) {
149 if (mStream == nullptr) {
150 LOG(ERROR) << __func__ << ": stream was closed";
151 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
152 }
153 mMetadata = metadata;
154 return !mIsStubStream ? mStream->updateMetadataCommon(metadata) : ndk::ScopedAStatus::ok();
155 }
156
initInstance(const std::shared_ptr<StreamCommonInterface> & delegate)157 ndk::ScopedAStatus StreamSwitcher::initInstance(
158 const std::shared_ptr<StreamCommonInterface>& delegate) {
159 mCommon = ndk::SharedRefBase::make<StreamCommonDelegator>(delegate);
160 // The delegate is null because StreamSwitcher handles IStreamCommon methods by itself.
161 return mStream->initInstance(nullptr);
162 }
163
getContext() const164 const StreamContext& StreamSwitcher::getContext() const {
165 return *mContext;
166 }
167
isClosed() const168 bool StreamSwitcher::isClosed() const {
169 return mStream == nullptr || mStream->isClosed();
170 }
171
getConnectedDevices() const172 const StreamCommonInterface::ConnectedDevices& StreamSwitcher::getConnectedDevices() const {
173 return mStream->getConnectedDevices();
174 }
175
setConnectedDevices(const std::vector<AudioDevice> & devices)176 ndk::ScopedAStatus StreamSwitcher::setConnectedDevices(const std::vector<AudioDevice>& devices) {
177 LOG(DEBUG) << __func__ << ": " << ::android::internal::ToString(devices);
178 if (mStream->getConnectedDevices() == devices) return ndk::ScopedAStatus::ok();
179 const DeviceSwitchBehavior behavior = switchCurrentStream(devices);
180 if (behavior == DeviceSwitchBehavior::UNSUPPORTED_DEVICES) {
181 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
182 } else if (behavior == DeviceSwitchBehavior::SWITCH_TO_STUB_STREAM && !devices.empty()) {
183 // This is an error in the extending class.
184 LOG(FATAL) << __func__
185 << ": switching to stub stream with connected devices is not allowed";
186 }
187 if (behavior == USE_CURRENT_STREAM) {
188 mIsStubStream = false;
189 } else {
190 LOG(DEBUG) << __func__ << ": connected devices changed, switching stream";
191 // Two streams can't be opened for the same context, thus we always need to close
192 // the current one before creating a new one.
193 RETURN_STATUS_IF_ERROR(closeCurrentStream(true /*validateStreamState*/));
194 if (behavior == CREATE_NEW_STREAM) {
195 mStream = createNewStream(devices, mContext, mMetadata);
196 mIsStubStream = false;
197 } else { // SWITCH_TO_STUB_STREAM
198 mStream.reset(new InnerStreamWrapper<StreamStub>(mContext, mMetadata));
199 mIsStubStream = true;
200 }
201 // The delegate is null because StreamSwitcher handles IStreamCommon methods by itself.
202 if (ndk::ScopedAStatus status = mStream->initInstance(nullptr); !status.isOk()) {
203 if (mIsStubStream) {
204 LOG(FATAL) << __func__
205 << ": failed to initialize stub stream: " << status.getDescription();
206 }
207 // Need to close the current failed stream, and report an error.
208 // Since we can't operate without a stream implementation, put a stub in.
209 RETURN_STATUS_IF_ERROR(closeCurrentStream(false /*validateStreamState*/));
210 mStream.reset(new InnerStreamWrapper<StreamStub>(mContext, mMetadata));
211 (void)mStream->initInstance(nullptr);
212 (void)mStream->setConnectedDevices(devices);
213 return status;
214 }
215 }
216 RETURN_STATUS_IF_ERROR(mStream->setConnectedDevices(devices));
217 if (behavior == CREATE_NEW_STREAM) {
218 // These updates are less critical, only log warning on failure.
219 if (mHwAvSyncId.has_value()) {
220 if (auto status = mStream->updateHwAvSyncId(*mHwAvSyncId); !status.isOk()) {
221 LOG(WARNING) << __func__ << ": could not update HW AV Sync for a new stream: "
222 << status.getDescription();
223 }
224 }
225 for (const auto& vndParam : mMissedParameters) {
226 if (auto status = mStream->setVendorParameters(vndParam.first, vndParam.second);
227 !status.isOk()) {
228 LOG(WARNING) << __func__ << ": error while setting parameters for a new stream: "
229 << status.getDescription();
230 }
231 }
232 mMissedParameters.clear();
233 for (const auto& effect : mEffects) {
234 if (auto status = mStream->addEffect(effect); !status.isOk()) {
235 LOG(WARNING) << __func__ << ": error while adding effect for a new stream: "
236 << status.getDescription();
237 }
238 }
239 if (mBluetoothParametersUpdated) {
240 if (auto status = mStream->bluetoothParametersUpdated(); !status.isOk()) {
241 LOG(WARNING) << __func__
242 << ": error while updating BT parameters for a new stream: "
243 << status.getDescription();
244 }
245 }
246 mBluetoothParametersUpdated = false;
247 }
248 return ndk::ScopedAStatus::ok();
249 }
250
bluetoothParametersUpdated()251 ndk::ScopedAStatus StreamSwitcher::bluetoothParametersUpdated() {
252 if (mStream == nullptr) {
253 LOG(ERROR) << __func__ << ": stream was closed";
254 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
255 }
256 if (mIsStubStream) {
257 mBluetoothParametersUpdated = true;
258 return ndk::ScopedAStatus::ok();
259 }
260 return mStream->bluetoothParametersUpdated();
261 }
262
263 } // namespace aidl::android::hardware::audio::core
264