blob: e00c34b50f4b4f7ff6225d937fd6a4c4c5cfac22 [file] [log] [blame]
Mikhail Naganov43a85cf2023-07-24 11:44:50 -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
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
28using aidl::android::hardware::audio::effect::IEffect;
29using aidl::android::media::audio::common::AudioDevice;
30
31namespace aidl::android::hardware::audio::core {
32
33StreamSwitcher::StreamSwitcher(StreamContext* context, const Metadata& metadata)
Shraddha Basantwani035beba2023-07-28 12:55:25 +000034 : mContext(context),
35 mMetadata(metadata),
36 mStream(new InnerStreamWrapper<StreamStub>(context, mMetadata)) {}
Mikhail Naganov43a85cf2023-07-24 11:44:50 -070037
38ndk::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
49ndk::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
60ndk::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
68ndk::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
78ndk::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
91ndk::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
104ndk::ScopedAStatus StreamSwitcher::addEffect(const std::shared_ptr<IEffect>& in_effect) {
Shraddha Basantwani035beba2023-07-28 12:55:25 +0000105 if (in_effect == nullptr) {
106 LOG(DEBUG) << __func__ << ": null effect";
107 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
108 }
Mikhail Naganov43a85cf2023-07-24 11:44:50 -0700109 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
120ndk::ScopedAStatus StreamSwitcher::removeEffect(const std::shared_ptr<IEffect>& in_effect) {
Shraddha Basantwani035beba2023-07-28 12:55:25 +0000121 if (in_effect == nullptr) {
122 LOG(DEBUG) << __func__ << ": null effect";
123 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
124 }
Mikhail Naganov43a85cf2023-07-24 11:44:50 -0700125 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();) {
130 if ((*it)->asBinder() == in_effect->asBinder()) {
131 it = mEffects.erase(it);
132 } else {
133 ++it;
134 }
135 }
136 return !mIsStubStream ? mStream->removeEffect(in_effect) : ndk::ScopedAStatus::ok();
137}
138
139ndk::ScopedAStatus StreamSwitcher::getStreamCommonCommon(
140 std::shared_ptr<IStreamCommon>* _aidl_return) {
141 if (!mCommon) {
142 LOG(FATAL) << __func__ << ": the common interface was not created";
143 }
144 *_aidl_return = mCommon.getInstance();
145 LOG(DEBUG) << __func__ << ": returning " << _aidl_return->get()->asBinder().get();
146 return ndk::ScopedAStatus::ok();
147}
148
149ndk::ScopedAStatus StreamSwitcher::updateMetadataCommon(const Metadata& metadata) {
150 if (mStream == nullptr) {
151 LOG(ERROR) << __func__ << ": stream was closed";
152 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
153 }
154 mMetadata = metadata;
155 return !mIsStubStream ? mStream->updateMetadataCommon(metadata) : ndk::ScopedAStatus::ok();
156}
157
158ndk::ScopedAStatus StreamSwitcher::initInstance(
159 const std::shared_ptr<StreamCommonInterface>& delegate) {
160 mCommon = ndk::SharedRefBase::make<StreamCommonDelegator>(delegate);
161 // The delegate is null because StreamSwitcher handles IStreamCommon methods by itself.
162 return mStream->initInstance(nullptr);
163}
164
165const StreamContext& StreamSwitcher::getContext() const {
166 return *mContext;
167}
168
169bool StreamSwitcher::isClosed() const {
170 return mStream == nullptr || mStream->isClosed();
171}
172
173const StreamCommonInterface::ConnectedDevices& StreamSwitcher::getConnectedDevices() const {
174 return mStream->getConnectedDevices();
175}
176
177ndk::ScopedAStatus StreamSwitcher::setConnectedDevices(const std::vector<AudioDevice>& devices) {
178 LOG(DEBUG) << __func__ << ": " << ::android::internal::ToString(devices);
179 if (mStream->getConnectedDevices() == devices) return ndk::ScopedAStatus::ok();
180 const DeviceSwitchBehavior behavior = switchCurrentStream(devices);
181 if (behavior == DeviceSwitchBehavior::UNSUPPORTED_DEVICES) {
182 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
183 } else if (behavior == DeviceSwitchBehavior::SWITCH_TO_STUB_STREAM && !devices.empty()) {
184 // This is an error in the extending class.
185 LOG(FATAL) << __func__
186 << ": switching to stub stream with connected devices is not allowed";
187 }
188 if (behavior == USE_CURRENT_STREAM) {
189 mIsStubStream = false;
190 } else {
191 LOG(DEBUG) << __func__ << ": connected devices changed, switching stream";
192 // Two streams can't be opened for the same context, thus we always need to close
193 // the current one before creating a new one.
194 RETURN_STATUS_IF_ERROR(closeCurrentStream(true /*validateStreamState*/));
195 if (behavior == CREATE_NEW_STREAM) {
196 mStream = createNewStream(devices, mContext, mMetadata);
197 mIsStubStream = false;
198 } else { // SWITCH_TO_STUB_STREAM
199 mStream.reset(new InnerStreamWrapper<StreamStub>(mContext, mMetadata));
200 mIsStubStream = true;
201 }
202 // The delegate is null because StreamSwitcher handles IStreamCommon methods by itself.
203 if (ndk::ScopedAStatus status = mStream->initInstance(nullptr); !status.isOk()) {
204 // Need to close the current failed stream, and report an error.
205 // Since we can't operate without a stream implementation, put a stub in.
206 RETURN_STATUS_IF_ERROR(closeCurrentStream(false /*validateStreamState*/));
207 mStream.reset(new InnerStreamWrapper<StreamStub>(mContext, mMetadata));
208 (void)mStream->initInstance(nullptr);
209 (void)mStream->setConnectedDevices(devices);
210 return status;
211 }
212 }
213 RETURN_STATUS_IF_ERROR(mStream->setConnectedDevices(devices));
214 if (behavior == CREATE_NEW_STREAM) {
215 // These updates are less critical, only log warning on failure.
216 if (mHwAvSyncId.has_value()) {
217 if (auto status = mStream->updateHwAvSyncId(*mHwAvSyncId); !status.isOk()) {
218 LOG(WARNING) << __func__ << ": could not update HW AV Sync for a new stream: "
219 << status.getDescription();
220 }
221 }
222 for (const auto& vndParam : mMissedParameters) {
223 if (auto status = mStream->setVendorParameters(vndParam.first, vndParam.second);
224 !status.isOk()) {
225 LOG(WARNING) << __func__ << ": error while setting parameters for a new stream: "
226 << status.getDescription();
227 }
228 }
229 mMissedParameters.clear();
230 for (const auto& effect : mEffects) {
231 if (auto status = mStream->addEffect(effect); !status.isOk()) {
232 LOG(WARNING) << __func__ << ": error while adding effect for a new stream: "
233 << status.getDescription();
234 }
235 }
236 }
237 return ndk::ScopedAStatus::ok();
238}
239
240} // namespace aidl::android::hardware::audio::core