blob: 8ba15a83e6fb2fed805c7f18aed217d3b716a77d [file] [log] [blame]
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <limits>
#define LOG_TAG "AHAL_StreamSwitcher"
#include <Utils.h>
#include <android-base/logging.h>
#include <error/expected_utils.h>
#include "core-impl/StreamStub.h"
#include "core-impl/StreamSwitcher.h"
using aidl::android::hardware::audio::effect::IEffect;
using aidl::android::media::audio::common::AudioDevice;
namespace aidl::android::hardware::audio::core {
StreamSwitcher::StreamSwitcher(StreamContext* context, const Metadata& metadata)
: mContext(context),
mMetadata(metadata),
mStream(new InnerStreamWrapper<StreamStub>(context, mMetadata)) {}
ndk::ScopedAStatus StreamSwitcher::closeCurrentStream(bool validateStreamState) {
if (!mStream) return ndk::ScopedAStatus::ok();
RETURN_STATUS_IF_ERROR(mStream->prepareToClose());
RETURN_STATUS_IF_ERROR(mStream->close());
if (validateStreamState && !isValidClosingStreamState(mStream->getStatePriorToClosing())) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
mStream.reset();
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus StreamSwitcher::close() {
if (mStream != nullptr) {
auto status = closeCurrentStream(false /*validateStreamState*/);
// The actual state is irrelevant since only StreamSwitcher cares about it.
onClose(StreamDescriptor::State::STANDBY);
return status;
}
LOG(ERROR) << __func__ << ": stream was already closed";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
ndk::ScopedAStatus StreamSwitcher::prepareToClose() {
if (mStream != nullptr) {
return mStream->prepareToClose();
}
LOG(ERROR) << __func__ << ": stream was closed";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
ndk::ScopedAStatus StreamSwitcher::updateHwAvSyncId(int32_t in_hwAvSyncId) {
if (mStream == nullptr) {
LOG(ERROR) << __func__ << ": stream was closed";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
RETURN_STATUS_IF_ERROR(mStream->updateHwAvSyncId(in_hwAvSyncId));
mHwAvSyncId = in_hwAvSyncId;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus StreamSwitcher::getVendorParameters(const std::vector<std::string>& in_ids,
std::vector<VendorParameter>* _aidl_return) {
if (mStream == nullptr) {
LOG(ERROR) << __func__ << ": stream was closed";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (mIsStubStream) {
LOG(ERROR) << __func__ << ": the stream is not connected";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
return mStream->getVendorParameters(in_ids, _aidl_return);
}
ndk::ScopedAStatus StreamSwitcher::setVendorParameters(
const std::vector<VendorParameter>& in_parameters, bool in_async) {
if (mStream == nullptr) {
LOG(ERROR) << __func__ << ": stream was closed";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (mIsStubStream) {
mMissedParameters.emplace_back(in_parameters, in_async);
return ndk::ScopedAStatus::ok();
}
return mStream->setVendorParameters(in_parameters, in_async);
}
ndk::ScopedAStatus StreamSwitcher::addEffect(const std::shared_ptr<IEffect>& in_effect) {
if (in_effect == nullptr) {
LOG(DEBUG) << __func__ << ": null effect";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
if (mStream == nullptr) {
LOG(ERROR) << __func__ << ": stream was closed";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (!mIsStubStream) {
RETURN_STATUS_IF_ERROR(mStream->addEffect(in_effect));
}
mEffects.push_back(in_effect);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus StreamSwitcher::removeEffect(const std::shared_ptr<IEffect>& in_effect) {
if (in_effect == nullptr) {
LOG(DEBUG) << __func__ << ": null effect";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
if (mStream == nullptr) {
LOG(ERROR) << __func__ << ": stream was closed";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
for (auto it = mEffects.begin(); it != mEffects.end(); ++it) {
if ((*it)->asBinder() == in_effect->asBinder()) {
mEffects.erase(it);
break;
}
}
return !mIsStubStream ? mStream->removeEffect(in_effect) : ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus StreamSwitcher::getStreamCommonCommon(
std::shared_ptr<IStreamCommon>* _aidl_return) {
if (!mCommon) {
LOG(FATAL) << __func__ << ": the common interface was not created";
}
*_aidl_return = mCommon.getInstance();
LOG(DEBUG) << __func__ << ": returning " << _aidl_return->get()->asBinder().get();
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus StreamSwitcher::updateMetadataCommon(const Metadata& metadata) {
if (mStream == nullptr) {
LOG(ERROR) << __func__ << ": stream was closed";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
mMetadata = metadata;
return !mIsStubStream ? mStream->updateMetadataCommon(metadata) : ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus StreamSwitcher::initInstance(
const std::shared_ptr<StreamCommonInterface>& delegate) {
mCommon = ndk::SharedRefBase::make<StreamCommonDelegator>(delegate);
// The delegate is null because StreamSwitcher handles IStreamCommon methods by itself.
return mStream->initInstance(nullptr);
}
const StreamContext& StreamSwitcher::getContext() const {
return *mContext;
}
bool StreamSwitcher::isClosed() const {
return mStream == nullptr || mStream->isClosed();
}
const StreamCommonInterface::ConnectedDevices& StreamSwitcher::getConnectedDevices() const {
return mStream->getConnectedDevices();
}
ndk::ScopedAStatus StreamSwitcher::setConnectedDevices(const std::vector<AudioDevice>& devices) {
LOG(DEBUG) << __func__ << ": " << ::android::internal::ToString(devices);
if (mStream->getConnectedDevices() == devices) return ndk::ScopedAStatus::ok();
const DeviceSwitchBehavior behavior = switchCurrentStream(devices);
if (behavior == DeviceSwitchBehavior::UNSUPPORTED_DEVICES) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
} else if (behavior == DeviceSwitchBehavior::SWITCH_TO_STUB_STREAM && !devices.empty()) {
// This is an error in the extending class.
LOG(FATAL) << __func__
<< ": switching to stub stream with connected devices is not allowed";
}
if (behavior == USE_CURRENT_STREAM) {
mIsStubStream = false;
} else {
LOG(DEBUG) << __func__ << ": connected devices changed, switching stream";
// Two streams can't be opened for the same context, thus we always need to close
// the current one before creating a new one.
RETURN_STATUS_IF_ERROR(closeCurrentStream(true /*validateStreamState*/));
if (behavior == CREATE_NEW_STREAM) {
mStream = createNewStream(devices, mContext, mMetadata);
mIsStubStream = false;
} else { // SWITCH_TO_STUB_STREAM
mStream.reset(new InnerStreamWrapper<StreamStub>(mContext, mMetadata));
mIsStubStream = true;
}
// The delegate is null because StreamSwitcher handles IStreamCommon methods by itself.
if (ndk::ScopedAStatus status = mStream->initInstance(nullptr); !status.isOk()) {
if (mIsStubStream) {
LOG(FATAL) << __func__
<< ": failed to initialize stub stream: " << status.getDescription();
}
// Need to close the current failed stream, and report an error.
// Since we can't operate without a stream implementation, put a stub in.
RETURN_STATUS_IF_ERROR(closeCurrentStream(false /*validateStreamState*/));
mStream.reset(new InnerStreamWrapper<StreamStub>(mContext, mMetadata));
(void)mStream->initInstance(nullptr);
(void)mStream->setConnectedDevices(devices);
return status;
}
}
RETURN_STATUS_IF_ERROR(mStream->setConnectedDevices(devices));
if (behavior == CREATE_NEW_STREAM) {
// These updates are less critical, only log warning on failure.
if (mHwAvSyncId.has_value()) {
if (auto status = mStream->updateHwAvSyncId(*mHwAvSyncId); !status.isOk()) {
LOG(WARNING) << __func__ << ": could not update HW AV Sync for a new stream: "
<< status.getDescription();
}
}
for (const auto& vndParam : mMissedParameters) {
if (auto status = mStream->setVendorParameters(vndParam.first, vndParam.second);
!status.isOk()) {
LOG(WARNING) << __func__ << ": error while setting parameters for a new stream: "
<< status.getDescription();
}
}
mMissedParameters.clear();
for (const auto& effect : mEffects) {
if (auto status = mStream->addEffect(effect); !status.isOk()) {
LOG(WARNING) << __func__ << ": error while adding effect for a new stream: "
<< status.getDescription();
}
}
if (mBluetoothParametersUpdated) {
if (auto status = mStream->bluetoothParametersUpdated(); !status.isOk()) {
LOG(WARNING) << __func__
<< ": error while updating BT parameters for a new stream: "
<< status.getDescription();
}
}
mBluetoothParametersUpdated = false;
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus StreamSwitcher::bluetoothParametersUpdated() {
if (mStream == nullptr) {
LOG(ERROR) << __func__ << ": stream was closed";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (mIsStubStream) {
mBluetoothParametersUpdated = true;
return ndk::ScopedAStatus::ok();
}
return mStream->bluetoothParametersUpdated();
}
} // namespace aidl::android::hardware::audio::core