Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 24d18248 authored by Mikhail Naganov's avatar Mikhail Naganov Committed by Automerger Merge Worker
Browse files

audio: Add a helper class to simplify legacy HALs migration am: 43a85cfb

parents 3b553744 43a85cfb
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -76,6 +76,7 @@ cc_library {
        "ModulePrimary.cpp",
        "SoundDose.cpp",
        "Stream.cpp",
        "StreamSwitcher.cpp",
        "Telephony.cpp",
        "alsa/Mixer.cpp",
        "alsa/ModuleAlsa.cpp",
+1 −2
Original line number Diff line number Diff line
@@ -670,8 +670,7 @@ ndk::ScopedAStatus StreamCommonImpl::close() {
        LOG(DEBUG) << __func__ << ": joining the worker thread...";
        mWorker->stop();
        LOG(DEBUG) << __func__ << ": worker thread joined";
        onClose();
        mWorker->setClosed();
        onClose(mWorker->setClosed());
        return ndk::ScopedAStatus::ok();
    } else {
        LOG(ERROR) << __func__ << ": stream was already closed";
+230 −0
Original line number Diff line number Diff line
/*
 * 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)
    : 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 (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 (mStream == nullptr) {
        LOG(ERROR) << __func__ << ": stream was closed";
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
    }
    for (auto it = mEffects.begin(); it != mEffects.end();) {
        if ((*it)->asBinder() == in_effect->asBinder()) {
            it = mEffects.erase(it);
        } else {
            ++it;
        }
    }
    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()) {
            // 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();
            }
        }
    }
    return ndk::ScopedAStatus::ok();
}

}  // namespace aidl::android::hardware::audio::core
+15 −7
Original line number Diff line number Diff line
@@ -66,7 +66,8 @@ class StreamContext {
            DataMQ;

    // Ensure that this value is not used by any of StreamDescriptor.State enums
    static constexpr int32_t STATE_CLOSED = -1;
    static constexpr StreamDescriptor::State STATE_CLOSED =
            static_cast<StreamDescriptor::State>(-1);

    struct DebugParameters {
        // An extra delay for transient states, in ms.
@@ -205,10 +206,14 @@ struct DriverInterface {

class StreamWorkerCommonLogic : public ::android::hardware::audio::common::StreamLogic {
  public:
    bool isClosed() const {
        return static_cast<int32_t>(mState.load()) == StreamContext::STATE_CLOSED;
    bool isClosed() const { return mState == StreamContext::STATE_CLOSED; }
    StreamDescriptor::State setClosed() {
        auto prevState = mState.exchange(StreamContext::STATE_CLOSED);
        if (prevState != StreamContext::STATE_CLOSED) {
            mStatePriorToClosing = prevState;
        }
        return mStatePriorToClosing;
    }
    void setClosed() { mState = static_cast<StreamDescriptor::State>(StreamContext::STATE_CLOSED); }
    void setIsConnected(bool connected) { mIsConnected = connected; }

  protected:
@@ -231,6 +236,9 @@ class StreamWorkerCommonLogic : public ::android::hardware::audio::common::Strea
    // which happens on the worker thread only.
    StreamContext* const mContext;
    DriverInterface* const mDriver;
    // This is the state the stream was in before being closed. It is retrieved by the main
    // thread after joining the worker thread.
    StreamDescriptor::State mStatePriorToClosing = StreamDescriptor::State::STANDBY;
    // Atomic fields are used both by the main and worker threads.
    std::atomic<bool> mIsConnected = false;
    static_assert(std::atomic<StreamDescriptor::State>::is_always_lock_free);
@@ -252,7 +260,7 @@ struct StreamWorkerInterface {
    virtual ~StreamWorkerInterface() = default;
    virtual bool isClosed() const = 0;
    virtual void setIsConnected(bool isConnected) = 0;
    virtual void setClosed() = 0;
    virtual StreamDescriptor::State setClosed() = 0;
    virtual bool start() = 0;
    virtual void stop() = 0;
};
@@ -267,7 +275,7 @@ class StreamWorkerImpl : public StreamWorkerInterface,
        : WorkerImpl(context, driver) {}
    bool isClosed() const override { return WorkerImpl::isClosed(); }
    void setIsConnected(bool isConnected) override { WorkerImpl::setIsConnected(isConnected); }
    void setClosed() override { WorkerImpl::setClosed(); }
    StreamDescriptor::State setClosed() override { return WorkerImpl::setClosed(); }
    bool start() override {
        return WorkerImpl::start(WorkerImpl::kThreadName, ANDROID_PRIORITY_AUDIO);
    }
@@ -459,7 +467,7 @@ class StreamCommonImpl : virtual public StreamCommonInterface, virtual public Dr
        };
    }

    virtual void onClose() = 0;
    virtual void onClose(StreamDescriptor::State statePriorToClosing) = 0;
    void stopWorker();

    const StreamContext& mContext;
+2 −2
Original line number Diff line number Diff line
@@ -81,7 +81,7 @@ class StreamInRemoteSubmix final : public StreamIn, public StreamRemoteSubmix {
            const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones);

  private:
    void onClose() override { defaultOnClose(); }
    void onClose(StreamDescriptor::State) override { defaultOnClose(); }
    ndk::ScopedAStatus getActiveMicrophones(
            std::vector<::aidl::android::media::audio::common::MicrophoneDynamicInfo>* _aidl_return)
            override;
@@ -97,7 +97,7 @@ class StreamOutRemoteSubmix final : public StreamOut, public StreamRemoteSubmix
                    offloadInfo);

  private:
    void onClose() override { defaultOnClose(); }
    void onClose(StreamDescriptor::State) override { defaultOnClose(); }
};

}  // namespace aidl::android::hardware::audio::core
Loading