Mikhail Naganov | 43a85cf | 2023-07-24 11:44:50 -0700 | [diff] [blame] | 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 | |
| 33 | StreamSwitcher::StreamSwitcher(StreamContext* context, const Metadata& metadata) |
| 34 | : mMetadata(metadata), mStream(new InnerStreamWrapper<StreamStub>(context, mMetadata)) {} |
| 35 | |
| 36 | ndk::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 | |
| 47 | ndk::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 | |
| 58 | ndk::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 | |
| 66 | ndk::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 | |
| 76 | ndk::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 | |
| 89 | ndk::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 | |
| 102 | ndk::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 | |
| 114 | ndk::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 | |
| 129 | ndk::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 | |
| 139 | ndk::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 | |
| 148 | ndk::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 | |
| 155 | const StreamContext& StreamSwitcher::getContext() const { |
| 156 | return *mContext; |
| 157 | } |
| 158 | |
| 159 | bool StreamSwitcher::isClosed() const { |
| 160 | return mStream == nullptr || mStream->isClosed(); |
| 161 | } |
| 162 | |
| 163 | const StreamCommonInterface::ConnectedDevices& StreamSwitcher::getConnectedDevices() const { |
| 164 | return mStream->getConnectedDevices(); |
| 165 | } |
| 166 | |
| 167 | ndk::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 |