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

Commit 9085c2bb authored by Mikhail Naganov's avatar Mikhail Naganov
Browse files

audio: Do not use StreamSwitcher for StreamPrimary

Since use of StreamSwitcher causes the worker thread to be changed
during connected device change, its use for the primary HAL streams
must be avoided. The reason is that switching of the FMQ reader thread
accompanied with simultaneous writes from two writers: one on the
framework side, another on the HAL side sending the "exit" command,
violates threading assumptions of blocking FMQ and causes spurious
races that eventually make FMQ non-functional.

Bug: 300130515
Bug: 368723297
Bug: 369272078
Bug: 369289912
Bug: 369964381
Test: atest VtsHalAudioCoreTargetTest
Merged-In: I14dc6fc08ae9e8aaaf3cd80e96b20dd1df54f633
Change-Id: I14dc6fc08ae9e8aaaf3cd80e96b20dd1df54f633
(cherry picked from commit f5ec73e5)
parent e4b4971a
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -76,6 +76,7 @@ cc_library {
        "r_submix/ModuleRemoteSubmix.cpp",
        "r_submix/SubmixRoute.cpp",
        "r_submix/StreamRemoteSubmix.cpp",
        "stub/DriverStubImpl.cpp",
        "stub/ModuleStub.cpp",
        "stub/StreamStub.cpp",
        "usb/ModuleUsb.cpp",
+49 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.
 */

#pragma once

#include "core-impl/Stream.h"

namespace aidl::android::hardware::audio::core {

class DriverStubImpl : virtual public DriverInterface {
  public:
    explicit DriverStubImpl(const StreamContext& context);

    ::android::status_t init() override;
    ::android::status_t drain(StreamDescriptor::DrainMode) override;
    ::android::status_t flush() override;
    ::android::status_t pause() override;
    ::android::status_t standby() override;
    ::android::status_t start() override;
    ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
                                 int32_t* latencyMs) override;
    void shutdown() override;

  private:
    const size_t mBufferSizeFrames;
    const size_t mFrameSizeBytes;
    const int mSampleRate;
    const bool mIsAsynchronous;
    const bool mIsInput;
    bool mIsInitialized = false;  // Used for validating the state machine logic.
    bool mIsStandby = true;       // Used for validating the state machine logic.
    int64_t mStartTimeNs = 0;
    long mFramesSinceStart = 0;
};

}  // namespace aidl::android::hardware::audio::core
+3 −0
Original line number Diff line number Diff line
@@ -132,6 +132,9 @@ class StreamContext {
    ReplyMQ* getReplyMQ() const { return mReplyMQ.get(); }
    int getTransientStateDelayMs() const { return mDebugParameters.transientStateDelayMs; }
    int getSampleRate() const { return mSampleRate; }
    bool isInput() const {
        return mFlags.getTag() == ::aidl::android::media::audio::common::AudioIoFlags::input;
    }
    bool isValid() const;
    // 'reset' is called on a Binder thread when closing the stream. Does not use
    // locking because it only cleans MQ pointers which were also set on the Binder thread.
+40 −23
Original line number Diff line number Diff line
@@ -16,10 +16,14 @@

#pragma once

#include <mutex>
#include <vector>

#include <android-base/thread_annotations.h>

#include "DriverStubImpl.h"
#include "StreamAlsa.h"
#include "StreamSwitcher.h"
#include "primary/PrimaryMixer.h"

namespace aidl::android::hardware::audio::core {

@@ -27,21 +31,54 @@ class StreamPrimary : public StreamAlsa {
  public:
    StreamPrimary(StreamContext* context, const Metadata& metadata);

    // Methods of 'DriverInterface'.
    ::android::status_t init() override;
    ::android::status_t drain(StreamDescriptor::DrainMode mode) override;
    ::android::status_t flush() override;
    ::android::status_t pause() override;
    ::android::status_t standby() override;
    ::android::status_t start() override;
    ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
                                 int32_t* latencyMs) override;
    ::android::status_t refinePosition(StreamDescriptor::Position* position) override;
    void shutdown() override;

    // Overridden methods of 'StreamCommonImpl', called on a Binder thread.
    ndk::ScopedAStatus setConnectedDevices(const ConnectedDevices& devices) override;

  protected:
    std::vector<alsa::DeviceProfile> getDeviceProfiles() override;
    bool isStubStream();

    const bool mIsAsynchronous;
    int64_t mStartTimeNs = 0;
    long mFramesSinceStart = 0;
    bool mSkipNextTransfer = false;

  private:
    using AlsaDeviceId = std::pair<int, int>;

    static constexpr StreamPrimary::AlsaDeviceId kDefaultCardAndDeviceId{
            primary::PrimaryMixer::kAlsaCard, primary::PrimaryMixer::kAlsaDevice};
    static constexpr StreamPrimary::AlsaDeviceId kStubDeviceId{
            primary::PrimaryMixer::kInvalidAlsaCard, primary::PrimaryMixer::kInvalidAlsaDevice};

    static AlsaDeviceId getCardAndDeviceId(
            const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices);
    static bool useStubStream(bool isInput,
                              const ::aidl::android::media::audio::common::AudioDevice& device);

    bool isStubStreamOnWorker() const { return mCurrAlsaDeviceId == kStubDeviceId; }

    DriverStubImpl mStubDriver;
    mutable std::mutex mLock;
    AlsaDeviceId mAlsaDeviceId GUARDED_BY(mLock) = kStubDeviceId;

    // Used by the worker thread only.
    AlsaDeviceId mCurrAlsaDeviceId = kStubDeviceId;
};

class StreamInPrimary final : public StreamIn, public StreamSwitcher, public StreamInHwGainHelper {
class StreamInPrimary final : public StreamIn, public StreamPrimary, public StreamInHwGainHelper {
  public:
    friend class ndk::SharedRefBase;
    StreamInPrimary(
@@ -50,14 +87,6 @@ class StreamInPrimary final : public StreamIn, public StreamSwitcher, public Str
            const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones);

  private:
    static bool useStubStream(const ::aidl::android::media::audio::common::AudioDevice& device);

    DeviceSwitchBehavior switchCurrentStream(
            const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices)
            override;
    std::unique_ptr<StreamCommonInterfaceEx> createNewStream(
            const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices,
            StreamContext* context, const Metadata& metadata) override;
    void onClose(StreamDescriptor::State) override { defaultOnClose(); }

    ndk::ScopedAStatus getHwGain(std::vector<float>* _aidl_return) override;
@@ -65,7 +94,7 @@ class StreamInPrimary final : public StreamIn, public StreamSwitcher, public Str
};

class StreamOutPrimary final : public StreamOut,
                               public StreamSwitcher,
                               public StreamPrimary,
                               public StreamOutHwVolumeHelper {
  public:
    friend class ndk::SharedRefBase;
@@ -75,22 +104,10 @@ class StreamOutPrimary final : public StreamOut,
                             offloadInfo);

  private:
    static bool useStubStream(const ::aidl::android::media::audio::common::AudioDevice& device);

    DeviceSwitchBehavior switchCurrentStream(
            const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices)
            override;
    std::unique_ptr<StreamCommonInterfaceEx> createNewStream(
            const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices,
            StreamContext* context, const Metadata& metadata) override;
    void onClose(StreamDescriptor::State) override { defaultOnClose(); }

    ndk::ScopedAStatus getHwVolume(std::vector<float>* _aidl_return) override;
    ndk::ScopedAStatus setHwVolume(const std::vector<float>& in_channelVolumes) override;

    ndk::ScopedAStatus setConnectedDevices(
            const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices)
            override;
};

}  // namespace aidl::android::hardware::audio::core
+2 −25
Original line number Diff line number Diff line
@@ -16,38 +16,15 @@

#pragma once

#include "core-impl/DriverStubImpl.h"
#include "core-impl/Stream.h"

namespace aidl::android::hardware::audio::core {

class StreamStub : public StreamCommonImpl {
class StreamStub : public StreamCommonImpl, public DriverStubImpl {
  public:
    StreamStub(StreamContext* context, const Metadata& metadata);
    ~StreamStub();

    // Methods of 'DriverInterface'.
    ::android::status_t init() override;
    ::android::status_t drain(StreamDescriptor::DrainMode) override;
    ::android::status_t flush() override;
    ::android::status_t pause() override;
    ::android::status_t standby() override;
    ::android::status_t start() override;
    ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
                                 int32_t* latencyMs) override;
    void shutdown() override;

  private:
    const size_t mBufferSizeFrames;
    const size_t mFrameSizeBytes;
    const int mSampleRate;
    const bool mIsAsynchronous;
    const bool mIsInput;
    bool mIsInitialized = false;  // Used for validating the state machine logic.
    bool mIsStandby = true;       // Used for validating the state machine logic.

    // Used by the worker thread.
    int64_t mStartTimeNs = 0;
    long mFramesSinceStart = 0;
};

class StreamInStub final : public StreamIn, public StreamStub {
Loading