• 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_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