263 lines
11 KiB
C++
263 lines
11 KiB
C++
/*
|
|
* 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
|