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